SpringSecurity+JWT配置的坑以及源碼分析

拋出問題:

之前在QQ上有位朋友問jwt的問題,開始的時候只給了他一個自己寫的參考案例,以為整個過程就可以順利配置并且可以愉快的使用,但是后面遇到了一個小坑,可能平時配置自定義AuthorizationServerConfigurerAdapter的自定義父類的時候,一下子就入坑了,好現(xiàn)在先拋出代碼然后分析,以下為部分核心配置代碼:

@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
            .authenticationManager(authenticationManager);
        //通過TokenEnhancerChain增強(qiáng)器鏈將jwtAccessTokenConverter(轉(zhuǎn)換成jwt)和jwtTokenEnhancer(往里面加內(nèi)容加信息)連起來
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancerList = Lists.newArrayList();
        enhancerList.add(tokenEnhancer());
        enhancerList.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(enhancerList);
        endpoints
            .tokenEnhancer(enhancerChain)
            .tokenServices(tokenServices())
            .accessTokenConverter(accessTokenConverter());

    }

    @Primary
    @Bean
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        //維持刷新token
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

   @Bean
    public TokenStore tokenStore() {
        TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
        return tokenStore;
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey(jwtSigningKey);
        return accessTokenConverter;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new CustomTokenEnhancer();
    }

可是啟動執(zhí)行獲取token接口返回值為:

{
    "access_token": "35cdbd07-9b5e-40ab-b69f-8eacaa2c1fc7",
    "token_type": "bearer",
    "refresh_token": "db5aa1e5-512b-45a4-9d55-aa137d710f67",
    "expires_in": 43199,
    "scope": "read,write"
}

乍一看這個沒啥問題,我明明配置的是jwt的accessTokenConvertertokenStore可是啟動程序生成的token怎么不是jwt呢,而是uuid的一串字符串,自己也沒發(fā)現(xiàn)有啥問題,找呀找呀,時間就這么過去了......

源碼分析:

通過跟蹤源碼分析TokenEndpoint的接口方法postAccessToken可以發(fā)現(xiàn)生成token的執(zhí)行的方法為:

    OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

其中其核心生成token的類為DefaultTokenServices執(zhí)行的方法為createAccessToken可以看到源碼中生成token的方法為createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken)的私有方法,源碼為:

private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
        DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
        int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
        if (validitySeconds > 0) {
            token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
        }
        token.setRefreshToken(refreshToken);
        token.setScope(authentication.getOAuth2Request().getScope());
        //這個才是自定義token生成策略的關(guān)鍵
        return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
    }

可以發(fā)現(xiàn)這邊在createToken的時候首先是使用UUID來生成的,然后在最后一行
accessTokenEnhancer != null采用的是accessTokenEnhancer配置的token生成策略,若為null則直接返回生成的uuidtoken.

失效原因分析:

源碼的token生成策略我們已經(jīng)分析完成,現(xiàn)在讓我們回過頭來看看我們之前的配置代碼TokenEnhancerChain已經(jīng)設(shè)置了我們的tokenStoreaccessTokenConverter但是為啥沒生效呢......?
可以看到我們在重寫configure(AuthorizationServerEndpointsConfigurer endpoints)這個方法的時候執(zhí)行了一個命令endpoints. .tokenServices(tokenServices())tokenService()方法我們配置的是什么呢?

@Primary
    @Bean
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        //維持刷新token
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

我們這邊重new DefaultTokenServices(),并且沒有配置DefaultTokenServices下的private TokenEnhancer accessTokenEnhancer;這個屬性,所以導(dǎo)致我們在DefaultTokenServices類下面執(zhí)行createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken)時最后面的accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;時accessTokenEnhancer為空,導(dǎo)致直接返回生成的UUID的token返回客戶端。

解決方案:

解決方式可以采用一下兩種方式:

  • configure(AuthorizationServerEndpointsConfigurer endpoints)方法中去掉endpoints.tokenServices(tokenServices())這個配置,刪除tokenService配置,只保留endpoint的配置。即:
@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
            .authenticationManager(authenticationManager);
        //通過TokenEnhancerChain增強(qiáng)器鏈將jwtAccessTokenConverter(轉(zhuǎn)換成jwt)和jwtTokenEnhancer(往里面加內(nèi)容加信息)連起來
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancerList = Lists.newArrayList();
        enhancerList.add(tokenEnhancer());
        enhancerList.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(enhancerList);
        endpoints
            .tokenEnhancer(enhancerChain)
            .accessTokenConverter(accessTokenConverter());

    }
  • endpoints配置 tokenEnhancer的配置代碼刪除,落到配置tokenService()方法的配置中去即:
 @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore())
            .authenticationManager(authenticationManager);
        endpoints
            .tokenServices(tokenServices())
            .accessTokenConverter(accessTokenConverter());

    }

    @Primary
    @Bean
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        //維持刷新token
        defaultTokenServices.setSupportRefreshToken(true);
        //通過TokenEnhancerChain增強(qiáng)器鏈將jwtAccessTokenConverter(轉(zhuǎn)換成jwt)和jwtTokenEnhancer(往里面加內(nèi)容加信息)連起來
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancerList = Lists.newArrayList();
        enhancerList.add(tokenEnhancer());
        enhancerList.add(accessTokenConverter());
        enhancerChain.setTokenEnhancers(enhancerList);
        defaultTokenServices.setTokenEnhancer(enhancerChain);
        return defaultTokenServices;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。