Spring Cloud微服務安全實戰_6-3_jwt認證之網關和服務改造


上一節在認證服務器里,將token 由uuid改造成了JWT,之前在網關上拿到令牌access_token后,需要去認證服務器校驗令牌,將令牌信息轉換為用戶信息。

現在有了jwt后,由於jwt是自包含的,已經包含了用戶的身份信息,所以在網關上不需要去認證服務器驗令牌了。

 

 

之前在網關上所做的這些去認證服務器驗令牌信息,轉換為用戶信息,去認證服務器做權限的判斷,這些其實SpringSecurity-OAuth都已經實現好了的。之前之所以手寫是為了理解SpringSecurity-OAuth內部的實現。

1,刪掉網關上filter包里的過濾器

 

 

 

2,在網關項目里加上依賴

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

3,配置獲取jwt驗簽的key的uri
認證服務器生成Jwt的時候,是進行了簽名的,有一個簽名的key;解析Jwt,需要驗簽,也需要這個key值,所以需要告訴網關,去哪里獲取這個簽名的key。
在網關的配置文件里配置: (這個uri具體在org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint里處理)
資源服務器啟動的時候,就會去認證服務器拿這個key,所以啟動網關前必須要保證認證服務器是啟動的。
security:
oauth2:
resource:
jwt:
key-uri: http://auth.nb.com:9090/oauth/token_key #獲取解析jwt,驗簽名key的路徑
client:
client-id: gateway #獲取驗簽key需要身份認證,這里是網關的clientId
client-secret: 123456 #獲取驗簽key需要身份認證,這里是網關的secret

4,網關作為一個資源服務器,配置其安全配置

/**
 * 作為一個資源服務器存在
 */
@Configuration
@EnableResourceServer
public class GatewaySecurityConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/token/**").permitAll() //放過/token開頭的請求,是在申請令牌
            .anyRequest().authenticated();
    }
}
5,各個微服務的改造
  
之前各個微服務需要用到用戶信息的時候,在網關上,網關從認證服務器解析令牌,獲取到用戶信息后,是將用戶名以明文的方式放在了請求頭里,各個微服務從請求頭里獲取到明文的username參數,這樣是有安全問題的。
  此時,各個微服務也需要解析jwt,獲取用戶信息。所以各個微服務就需要跟網關一樣,解析jwt,所以也需要從認證服務器獲取驗簽的key,故需要做和網關一樣的配置。
  在訂單微服務,引入 SpringSecurity-OAuth依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

 訂單微服務也作為資源服務器存在,所以需要給訂單微服務打上資源服務器的標記    @EnableResourceServer

 

 

 

在訂單微服務,獲取用戶信息 也要用注解 @AuthenticationPrincipal String username 。
實驗:

1,先啟動 認證服務器,因為各個微服務啟動的時候就會去認證服務器拿jwt驗簽的key。
2.啟動網關。
發現報如下異常 Caused by: org.springframework.web.client.HttpClientErrorException$NotFound: 404 null

 

這個異常的意思是,在拿jwt驗簽key的時候,找不到該服務,為什么呢?在認證服務器上,配置jwt tokenStore的時候,需要做一下特殊處理:
需要將
JwtAccessTokenConverter 暴露為Spring的Bean,而且必須為public的。

 

 
        

 

再次重啟認證服務器,重啟網關,正常啟動。

postman調用網關,獲取令牌 http://localhost:9070/token/oauth/token

客戶端信息表

 

 

 

 

 通過網關,拿access_token去訂單服務創建訂單  http://localhost:9070/order/orders

 

 

 處理辦法:

1,在oauth_client_details表里的orderApp應用的resource_ids 字段里,配置上網關的resourceId,前提是網關代碼里也配置了resourceId。這樣安全性更高,只是需要維護這些資源服務器id。

     

調用成功

 

 

 

 

 2,如果對安全性要求不高,可以把oauth_client_details表里 orderApp的resource_ids 字段設置為空,這樣給orderApp客戶端發出的jwt就可以訪問任何的微服務了。

 在微服務之間傳遞jwt令牌信息

比如訂單微服務調用了加個微服務,那么訂單微服務怎么把用戶信息傳遞給價格微服務呢?之前的做法是,將用戶信息放在請求頭里,進行傳遞。

其實SpringSecurity-OAuth已經替你把這些事情做好了。可以在微服務之間傳遞jwt令牌,傳過去的jwt會被解析為用戶信息。

下面改造訂單微服務和價格微服務,在他們之間傳遞用戶信息。

價格微服務也作為資源服務器存在

@SpringBootApplication
@EnableResourceServer//作為資源服務器
public class NbPriceApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(NbPriceApiApplication.class, args);
        System.err.println("============= Price Api 啟動完成 ============");
    }

}

價格微服務也加入SpringCloud和SpringSecurity-OAuth的依賴

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
dependencyManagement里:
<!--spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>

價格微服務獲取用戶信息 :   @AuthenticationPrincipal String username

能在微服務之間自動傳遞jwt令牌信息, 主角就是:OAuth2RestTemplate,它能從請求上下文中拿到jwt 令牌,然后將其放入請求頭,在其他微服務里,就可以通過@AuthenticationPrincipal 注解來獲得用戶信息了。

在訂單微服務里配置OAuth2RestTemplate

@Configuration
@SpringBootApplication
@EnableResourceServer//作為資源服務器存在
public class NbOrderApiApplication {

    //聲名OAuth2RestTemplate
    //會從請求的上下文里拿到令牌,放到請求頭里,發出去。需要兩個參數,springboot會自動出入進來
    @Bean
    public OAuth2RestTemplate oAuth2RestTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context){
        return new OAuth2RestTemplate(resource,context);
    }

    public static void main(String[] args) {
        SpringApplication.run(NbOrderApiApplication.class, args);
        System.err.println("============= Order Api 啟動完成 ============");
    }

}

 

 

 

 

隨便用某個客戶端生成一個jwt令牌

 

 

 

 然后通過網關調用創建訂單服務器

 

 

 

 查看訂單服務和價格服務里,是否打印了用戶名

訂單服務日志,網關已經把jwt傳遞給了訂單服務,而且訂單服務把jwt解析成了用戶信息

 

 

 查看價格微服務,看訂單服務是否把jwt傳給了價格服務

 

 

 至此,在網關上已經實現了由SpringSecurity-OAuth替我們實現各種認證啊、授權的過濾器。前面系列文章說的都是自己實現這些過濾器,自己實現是為了了解其中的原理。

目前的架構,前面文章說的兩個問題:

1,在網關上再去認證服務器驗令牌,認證服務器壓力變大

  解決:token 信息是 jwt,已經自包含身份信息。不用再去認證服務器驗令牌。減少了一次請求,網關、認證服務器的壓力減小了。

2,明文在微服務之間的請求頭里傳遞用戶信息

  解決:JWT是自包含身份信息的,用OAuth2RestTemplate發請求,SpringSecurity-OAuth會自動從請求上下文拿到jwt信息,放進請求頭,下游微服務拿到后會解析jwt。

到目前來說,各個微服務(包括網關),都是資源服務器,需要在它們配置類上打上 @EnableResourceServer 注解,使其成為資源服務器。而且各個資源服務器需要引入SpringCloud的依賴以及 spring-cloud-starter-oauth2 依賴。

目前的架構圖是這樣的

 

 

代碼:https://github.com/lhy1234/springcloud-security/tree/chapt-6-2-jwt02

歡迎關注個人公眾號一起交流學習:

 
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM