<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • spring security oauth2搭建resource-server demo及token改造成JWT令牌

    我們在上文講了如何在spring security的環境中搭建基于oauth2協議的認證中心demo:http://www.tnepal.com/process-h/p/15688971.html, 對應的還應該要有一個resource server。本章主要就是講resource server的demo搭建,并且再將普通的token改造成JWT令牌的形式以及為什么要改成JWT令牌格式。

    自定義resource-server實現類

    ? 在搭建oauth2認證中心時,我們需要再自定義一個繼承AuthorizationServerConfigurerAdapter的實現類,同時在該實現類上添加@EnableAuthorizationServer注解。同樣的,我們也需要創建一個繼承ResourceServerConfigurerAdapter的實現類,并在該實現類上添加@EnableResourceServer注解來聲明這是一個資源服務。

    @Configuration
    @EnableResourceServer
    public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    }
    

    ? 我們來看看ResourceServerConfigurerAdapter父類,發現它重寫了2個方法。以下是這2個方法中的參數對象可以配置的屬性:

    ResourceServerSecurityConfigurer中主要包括:

    • tokenServices:ResourceServerTokenServices 類的實例,用來實現令牌服務。
    • tokenStore:TokenStore類的實例,指定令牌如何訪問。
    • resourceId:這個資源服務的ID,這個屬性是可選的,但是推薦設置并在授權服務中進行驗證。
    • 其他的拓展屬性例如 tokenExtractor 令牌提取器用來提取請求中的令牌。

    HttpSecurity配置這個與Spring Security類似:

    • 請求匹配器,用來設置需要進行保護的資源路徑,默認的情況下是保護資源服務的全部路徑。
    • 通過http.authorizeRequests()來設置受保護資源的訪問規則。
    • 其他的自定義權限保護規則通過 HttpSecurity 來進行配置。
    public class ResourceServerConfigurerAdapter implements ResourceServerConfigurer {
       @Override
       public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
       }
       @Override
       public void configure(HttpSecurity http) throws Exception {
          http.authorizeRequests().anyRequest().authenticated();
       }
    
    }
    

    編寫ResourceServerConfig

    package com.peter.security.order.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.http.SessionCreationPolicy;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
    import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
    
    /**
     * @author huang
     * @description
     * @date 2021/12/17
     **/
    @Configuration
    @EnableResourceServer
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    
        public static final String RESOURCE_ID = "res1";
    
        //資源服務令牌解析服務
        @Bean
        public ResourceServerTokenServices tokenService() {
            //使用遠程服務請求授權服務器校驗token,必須指定校驗token 的url、client_id,client_secret
            RemoteTokenServices service=new RemoteTokenServices();
           service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
            // 聲明只有該client 的接入方才能訪問該資源服務
            service.setClientId("c1");
            service.setClientSecret("secret");
            return service;
        }
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            // 聲明該資源服務id,以及認證的tokenSerivce對象
            resources.resourceId(RESOURCE_ID)
                    .tokenServices(tokenService());
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
    // 訪問該資源服務的client的scope要有all權限                
                	.antMatchers("/**").access("#oauth2.hasScope('all')")
                    .and().csrf().disable()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }
    

    定義資源服務接口

    ? 有了配置,還需要暴露出一些資源接口給外部調用,我們這里簡單定義一個接口。

    package com.peter.security.order.controller;
    
    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @author huangyizeng
     * @description
     * @date 2021/12/17
     **/
    @RestController
    public class OrderController {
    
        // 訪問該資源的用戶要有P1權限
        @GetMapping(value = "/r1")
        @PreAuthorize("hasAnyAuthority('p1')")
        public String r1(){
            return "訪問資源1";
        }
    
    }
    

    示例

    1、申請token

    ? 我們知道Oauth2協議有4中授權方式,分別是:

    http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=hyz&password=hyz

    2、查看token代表了哪些信息

    ? spring security oauth2 中有個用于資源服務訪問的令牌解析端點(/oauth/check_token )。我們先來看看上面返回的token中都有哪些權限,訪問

    http://localhost:53020/uaa/oauth/check_token?token=c46ffcf5-3043-4513-9405-808f89b02f30

    ? 可以看到返回以下屬性信息:

    • 第三方client的基本信息:user_name\client_Id\scope
    • 第三方client的授權列表:authorities
    • 第三方client的資源服務列表:res1

    如果我們傳入一個不存在的token,那么將會返回

    {
        "error": "invalid_token",
        "error_description": "Token was not recognised"
    }
    

    3、使用token訪問資源服務

    ? 最后我們再來使用該token訪問資源服務。oauth2.0 要求將token根據具體的格式放在請求頭中:Authorization: Bearer token

    ? 如上圖,資源服務在接收到token參數后,會到授權服務的/oauth/check_token端點拿到該token的權限,然后與資源服務定義的授權進行比對,如果符合就繼續執行,否則返回

    {
        "error": "invalid_token",
        "error_description": "c46ffcf5-3043-4513-9405-808f89b02f301"
    }
    

    JWT令牌

    ? 通過上面的測試,當資源服務和授權服務不在一起時,資源服務需要通過網絡請求去授權服務的/token/check_token端點請求驗證token,如果訪問量較大將會影響系統的性能。

    ? 為了解決性能問題,可以將token令牌采用jwt格式,這樣用戶授權后通過后將會拿到一個JWT令牌,JWT令牌中已經包含了用戶相關的信息,包括權限,基本信息等,接著資源服務根據事先跟認證服務約定好的算法自行進行令牌校驗,無需每次都想認證服務請求驗證。

    ? JWT具體是什么,相信童鞋們也有一定的認識了,這里不過多說明。主要說明一點,一般JWT是是用非對稱加密使用的,在這里我們使用對稱加密的方式,省去非對稱加密密鑰生成步驟。

    1、配置認證服務JWT令牌

    ? 在認證服務中配置JWT令牌,即可實現生成JWT格式的令牌。

    TokenConfig ,修改tokenService的實現類。

    @Configuration
    public class TokenConfig {
    
        private String SIGNING_KEY = "uaa123";
        @Bean
        public TokenStore tokenStore() {
            return new JwtTokenStore(accessTokenConverter());
        }
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey(SIGNING_KEY); //對稱秘鑰,資源服務器使用該秘鑰來驗證
            return converter;
        }
    }
    

    定義使用JWT令牌

    public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
        @Autowired
        private JwtAccessTokenConverter accessTokenConverter;
         @Bean
        public AuthorizationServerTokenServices tokenService() {
            DefaultTokenServices service=new DefaultTokenServices();
            service.setClientDetailsService(clientDetailsService);
            service.setSupportRefreshToken(true);
            // 定義使用JWT令牌
            service.setTokenStore(tokenStore);
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
            service.setTokenEnhancer(tokenEnhancerChain);
            service.setAccessTokenValiditySeconds(7200); // 令牌默認有效期2小時
            service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默認有效期3天
            return service;
        }
    }
    

    檢驗令牌,根據密碼授權模式來獲取令牌:

    ? 由上圖可見,生成的令牌確實已經是JWT的格式了。

    ? 再查看該令牌中代表了哪些信息。訪問

    http://localhost:53020/uaa/oauth/check_token?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ7XCJmdWxsbmFtZVwiOlwiaHl6XCIsXCJpZFwiOlwiMVwiLFwicGFzc3dvcmRcIjpcIiQyYSQxMCRJdm94aUN3eEdXWkdFemxkcVY2bktPcUxuY05zMmNVdkdsQ2ZmZ1V0a2hZaGc4dUo0akd5eVwiLFwidXNlcm5hbWVcIjpcImh5elwifSIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2Mzk4MTk4ODgsImF1dGhvcml0aWVzIjpbInAxIiwicDMiXSwianRpIjoiNDAzNWU3NTItNmYzNS00Y2RhLTg0ZTgtNjEyZmFhZWI2OWFkIiwiY2xpZW50X2lkIjoiYzEifQ.VIzqACiYJ-ZCTrq1BkOM_HeXkTwE9tAZ3TSiy7B9pJ4

    確實訪問了在上面實例中的client相關信息:

    {
        "aud": [
            "res1"
        ],
        "user_name": "{\"fullname\":\"hyz\",\"id\":\"1\",\"password\":\"$2a$10$IvoxiCwxGWZGEzldqV6nKOqLncNs2cUvGlCffgUtkhYhg8uJ4jGyy\",\"username\":\"hyz\"}",
        "scope": [
            "all"
        ],
        "exp": 1639819888,
        "authorities": [
            "p1",
            "p3"
        ],
        "jti": "4035e752-6f35-4cda-84e8-612faaeb69ad",
        "client_id": "c1"
    }
    

    ? 由此可見,基于JWT的改造已經完成。

    2、修改資源服務檢驗令牌配置

    資源服務需要和授權服務擁有一致的簽字、令牌服務等:

    1. 將認證服務中的TokenConfig類拷貝到資源 服務中
    2. 將授權服務中的TokenConfig類拷貝到資源 服務中
    public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
    
        public static final String RESOURCE_ID = "res1";
    
        @Autowired
        private TokenStore tokenStore;
    
        //資源服務令牌解析服務
    //    @Bean
    //    public ResourceServerTokenServices tokenService() {
    //        //使用遠程服務請求授權服務器校驗token,必須指定校驗token 的url、client_id,client_secret
    //        RemoteTokenServices service=new RemoteTokenServices();
    //        service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
    //        service.setClientId("c1");
    //        service.setClientSecret("secret");
    //        return service;
    //    }
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(RESOURCE_ID)
                    .tokenStore(tokenStore);
        }
    }
    

    驗證令牌是否有效

    ? 我們再次使用上面基于JWT生成的令牌去訪問資源服務:

    ? 由上圖可知是可以正常訪問的,如果該令牌有誤,則會返回

    {
        "error": "invalid_token",
        "error_description": "Cannot convert access token to JSON"
    }
    

    ? 到這里,本篇就結束了,我們總結一下:

    1. 使用基本的token,需要去認證中心驗證token是否有效等,在高并發時會有性能問題
    2. 令牌改成JWT格式,解決了性能問題,當然也可以考慮將令牌存放在redis中也可以解決性能問題
    3. 本文栗子中使用對稱加密的JWT令牌,生產環境肯定是使用非對稱加密的密鑰對(如何配置,童鞋們可以實踐一下)

    ? spring security相關的內容就寫到這里,在spring security 及Oauth2的源碼中發現了許多不明白的地方,之前spring的源碼由粗略看到一部分,現在覺得還是需要再回頭看看spring源碼,所以后續會分享spring源碼的章節。

    posted @ 2021-12-18 16:34  尋找的路上  閱讀(1176)  評論(2編輯  收藏  舉報
    国产美女a做受大片观看