29.SpringSecurity-令牌配置

前言

image.png

  1. 到目前爲止,咱們已經把自定義的3種方式嫁接到認證服務器上,經過這種方式咱們能夠了發送OAuth令牌。目前token的生成和存儲是按照Spring默認的一套機制運做。可是這種默認的機制並不符合咱們的需求。
  2. 從這一節開始咱們看一下對Token處理的相關知識點。怎樣定製這個token,怎樣生成和存儲這個token。
  3. 咱們主要講解3個基本點:
    a.基本的Token參數配置
    b.使用JWT替換默認的Token
    c.擴展和解析JWT的信息

內容

1.基本的Token參數配置

咱們以前的token生成處理都是在Authentication Server裏面作的處理。 首先咱們須要修改咱們以前定義的Authentication Server:MyAuthorizationServerConfig以前是使用註解@EnableAuthorizationServer開啓默認的Spring Security OAuth的默認配置。咱們如今繼承:AuthorizationServerConfigurerAdapter,其有針對3個不一樣的默認的配置:
AuthorizationServerEndpointsConfigurer:端點配置
ClientDetailsServiceConfigurer:針對於第三方客戶端配置;所謂的客戶端就是有哪些應用會訪問咱們的系統,咱們的認證服務器會決定給哪些第三方應用client去發送令牌。若是這個配置後咱們以前配置文件中配置的clientId和clientSecret將不會起做用了 AuthorizationServerSecurityConfigurer:針對於安全性配置 前端

image.png

以前咱們進行認證時候,發送的/oauth/token請求剛好是發送到TokenEndpoint(他是處理咱們oauth請求的);AuthorizationServerEndpointsConfigurer的配置其實就是針對於TokenEndpoint作一些端點配置。對AuthorizationServerEndpointsConfigurer進行配置時候,須要有對象:AuthenticationManager、UserDetailsService。咱們本身不定義配置AuthorizationServerEndpointsConfigurer的時候,系統會默認去應用找到其對應的屬性:AuthenticationManager、UserDetailsService;當咱們想使用本身的邏輯時候,將其屬性設置成咱們本身定義的bean 本身覆蓋其配置。redis

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * 系統端點配置:endpoints
         * 1.使用咱們本身的受權管理器(AuthenticationManager)和自定義的用戶詳情服務(UserDetailsService)
         */
         endpoints.authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 系統第三方客戶端配置:
         * 所謂的客戶端就是有哪些應用會訪問咱們的系統,
         * 咱們的認證服務器會決定給哪些第三方應用client去發送令牌。
         * 若是這個配置後咱們以前配置文件中配置的clientId和clientSecret將不會起做用了
         */
        //目前咱們的應用場景是在咱們的app和咱們的前端;咱們不容許第三方來註冊,因此用內存
        clients.inMemory().withClient("yxm")
                .secret("yxmsecret")
                .accessTokenValiditySeconds(7200)//發出去的令牌,有效期是多少? 這裏設置爲2小時
                .authorizedGrantTypes("refresh_token","password")//針對當前應用客戶端:yxm,所能支持的受權模式是哪些?以前設置有4種類加上刷新總共5種:這裏只支持配置的:"refresh_token","password"。
                .scopes("all","read","write");//發出去的權限有哪些?以前前端請求攜帶了scoope,此配置的scope用來指定前端發送scope的值必須在配置的裏面或者不攜帶scope;默認爲此處配置的scope
    }
}

而後咱們重啓服務:嘗試下發送請求的效果. spring

image.png

返回結果中:expires_in是7199,剛好是咱們配置的7200s以內。
scope返回的是咱們client端配置的:"all read write"數據庫

{
"access_token": "e9cc6ce9-d241-44d9-9df3-dcfbf5ab1181",
"token_type": "bearer",
"refresh_token": "996949f9-d2ba-4d26-8456-1b678c5b1cf2",
"expires_in": 7199,
"scope": "all read write"
}

針對於client端配置的:authorizedGrantTypes("refresh_token","password")
咱們嘗試使用:implicit json

image.png

咱們發送下:scope在客戶端不存在的類型:xxx
image.png數組

咱們不發送scope
image.png安全

若是咱們除了給yxm對應的client端受權外,咱們還須要給其餘客戶端受權。直接在後面加上and拼接便可。
image.png服務器

可是又回到咱們以前說的呢?咱們這個模塊應該是通用的模塊,只須要配置便可實現接入。
OAuth2ClientPropertiesapp

public class OAuth2ClientProperties {
    //咱們把:ClientDetailsServiceConfigurer相關配置加進來
    private String clientId;
    private String clientSecret;
    private int accessTokenValiditySeconds;

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getClientSecret() {
        return clientSecret;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    public int getAccessTokenValiditySeconds() {
        return accessTokenValiditySeconds;
    }

    public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
        this.accessTokenValiditySeconds = accessTokenValiditySeconds;
    }
}

OAuth2Properties:ide

public class OAuth2Properties {
    private OAuth2ClientProperties[] clients = {};//默認空數組  

    public OAuth2ClientProperties[] getClients() {
        return clients;
    }

    public void setClients(OAuth2ClientProperties[] clients) {
        this.clients = clients;
    }
}

而後將其加入到security的配置下去:SecurityProperties

image.png

此時咱們去除demo裏面以前的默認的spring security oauth2客戶端配置,改成對應的配置。

#security:
#    oauth2:
#      client:
#        clientId: yxm
#        client_secret: yxmsecret

image.png

認證服務器裏面修改爲配置:

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SecurityProperties securityProperties;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * 系統端點配置:endpoints
         * 1.使用咱們本身的受權管理器(AuthenticationManager)和自定義的用戶詳情服務(UserDetailsService)
         */
         endpoints.authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 系統第三方客戶端配置:
         * 所謂的客戶端就是有哪些應用會訪問咱們的系統,
         * 咱們的認證服務器會決定給哪些第三方應用client去發送令牌。
         * 若是這個配置後咱們以前配置文件中配置的clientId和clientSecret將不會起做用了
         */
        //目前咱們的應用場景是在咱們的app和咱們的前端;咱們不容許第三方來註冊,因此用內存
       /* clients.inMemory().withClient("yxm")
                .secret("yxmsecret")
                .accessTokenValiditySeconds(7200)//發出去的令牌,有效期是多少? 這裏設置爲2小時
                .authorizedGrantTypes("refresh_token","password")//針對當前應用客戶端:yxm,所能支持的受權模式是哪些?以前設置有4種類加上刷新總共5種:這裏只支持配置的:"refresh_token","password"。
                .scopes("all","read","write")//發出去的權限有哪些?以前前端請求攜帶了scoope,此配置的scope用來指定前端發送scope的值必須在配置的裏面或者不攜帶scope;默認爲此處配置的scope
                .and()
                .withClient("startshineye")
                .secret("startshineyesecret")
                .accessTokenValiditySeconds(3600)
                .authorizedGrantTypes("password")
                .scopes("read");*/
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {//判斷咱們的配置是否爲空
            for (OAuth2ClientProperties client:securityProperties.getOauth2().getClients()){
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        .scopes("all");
            }
        }
    }
}

咱們如今使用第二次配置:startshineye,並將其accessTokenValiditySeconds配置

image.png

image.png

咱們如今使用第二次配置:startshineye,並將其accessTokenValiditySeconds不配置,返回的json對象中沒有令牌:

2.令牌存儲

咱們如今的令牌是存儲到內存中的,一旦咱們服務重啓的話。內存中的令牌就丟失了,因此咱們須要將令牌存儲到持久化的數據庫或者redis中,因爲令牌是訪問比較頻繁的,因此咱們將其配置到redis中。使用redis你不用去維護表的結構而且過時時間自動設置且性能比數據庫快不少。

咱們以前將到過TokenStore,他是用來負責令牌的存取,如今咱們寫一個TokenStoreConfig配置類。

@Configuration
public class TokenStoreConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean
    public TokenStore redisTokenStore(){
      return new RedisTokenStore(redisConnectionFactory);
    }
}

在咱們的認證服務器上引入自定義TokenStore:

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private TokenStore redisTokenStore;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        /**
         * 系統端點配置:endpoints
         * 1.使用咱們本身的受權管理器(AuthenticationManager)和自定義的用戶詳情服務(UserDetailsService)
         */
         endpoints.tokenStore(redisTokenStore)
                 .authenticationManager(authenticationManager)
                 .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 系統第三方客戶端配置:
         * 所謂的客戶端就是有哪些應用會訪問咱們的系統,
         * 咱們的認證服務器會決定給哪些第三方應用client去發送令牌。
         * 若是這個配置後咱們以前配置文件中配置的clientId和clientSecret將不會起做用了
         */
        //目前咱們的應用場景是在咱們的app和咱們的前端;咱們不容許第三方來註冊,因此用內存
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if(ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {//判斷咱們的配置是否爲空
            for (OAuth2ClientProperties client:securityProperties.getOauth2().getClients()){
                builder.withClient(client.getClientId())
                        .secret(client.getClientSecret())
                        .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                        .authorizedGrantTypes("refresh_token", "authorization_code", "password")
                        .scopes("all");
            }
        }
    }
}

測試:

image.png

咱們從redis裏面能夠查看到對應的信息。
image.png

相關文章
相關標籤/搜索