拋出問題:
之前在QQ上有位朋友問jwt的問題闲询,開始的時候只給了他一個自己寫的參考案例久免,以為整個過程就可以順利配置并且可以愉快的使用,但是后面遇到了一個小坑扭弧,可能平時配置自定義AuthorizationServerConfigurerAdapter
的自定義父類的時候阎姥,一下子就入坑了,好現(xiàn)在先拋出代碼然后分析鸽捻,以下為部分核心配置代碼:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
//通過TokenEnhancerChain增強器鏈將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的accessTokenConverter
和tokenStore
可是啟動程序生成的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
則直接返回生成的uuid
的token
.
失效原因分析:
源碼的token
生成策略我們已經(jīng)分析完成托猩,現(xiàn)在讓我們回過頭來看看我們之前的配置代碼TokenEnhancerChain
已經(jīng)設(shè)置了我們的tokenStore
和accessTokenConverter
但是為啥沒生效呢......?
可以看到我們在重寫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增強器鏈將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增強器鏈將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;
}