拓展token其实就是让生成token的map信息更多
但是目前调用细节我还不清楚
- 生成token(DefaultTokenServices类),然后调用tokenEnhancer链进行增强,jwt令牌的加强就是在这里做的
private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
//可以看源码,token的value就是简单的uuid
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());
return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}
- 从上一段代码的最后一行进入(accessTokenEnhancer.enhance(token, authentication)),JwtAccessTokenConverter.java中
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
Map<String, Object> info = new LinkedHashMap<String, Object>(accessToken.getAdditionalInformation());
String tokenId = result.getValue();
if (!info.containsKey(TOKEN_ID)) {
info.put(TOKEN_ID, tokenId);
}
else {
tokenId = (String) info.get(TOKEN_ID);
}
//将扩展的信息放入
result.setAdditionalInformation(info);
//通过这里生成jwt令牌并替换原来的uuid value
result.setValue(encode(result, authentication));
...
return result;
}
- 从上一段代码的倒数第二句进入到调用链底层,DefaultAccessTokenConverter.java中,根据原有的token与authentication转化为生成token(这里是jwt)的原料map
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
Map<String, Object> response = new HashMap<String, Object>();
OAuth2Request clientToken = authentication.getOAuth2Request();
...
//生成jwt令牌时放入扩展信息
response.putAll(token.getAdditionalInformation());
...
return response;
}
- 从上上段代码的倒数第二句进入,利用上一段代码的结果,根据map生成jwt令牌,JwtAccessTokenConverter.java中
protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String content;
try {
content = objectMapper.formatMap(tokenConverter.convertAccessToken(accessToken, authentication));
}
catch (Exception e) {
throw new IllegalStateException("Cannot convert access token to JSON", e);
}
String token = JwtHelper.encode(content, signer).getEncoded();
return token;
}
从这里我们可以知道,在DefaultAccessTokenConverter.java中会将额外信息放入map中,response.putAll(token.getAdditionalInformation());
,所以想要扩展jwt令牌的话,就要在生成jwt令牌前,将额外信息添加到token的的AdditionalInformation中,也就是 setAdditionalInformation
例子:
@Slf4j
@Component("MyTokenEnhancer")
public class MyTokenEnhancer implements TokenEnhancer {
@Autowired
private ObjectMapper objectMapper;
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
Map<String,Object> additionalInfo = new HashMap<>();
Object principal = authentication.getPrincipal();
try {
String s = objectMapper.writeValueAsString(principal);
log.info("{}",s);
Map map = objectMapper.readValue(s, Map.class);
map.remove("password");
map.remove("authorities");
map.remove("accountNonExpired");
map.remove("accountNonLocked");
map.remove("credentialsNonExpired");
map.remove("enabled");
additionalInfo.put("user_info",map);
//这一句话,放入AdditionalInformation中
((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInfo);
} catch (IOException e) {
log.error("",e);
}
return accessToken;
}
}
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Bean(name="authorizationServerTokenServicesCustom")
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service=new DefaultTokenServices();
service.setSupportRefreshToken(true);//支持刷新令牌
service.setTokenStore(tokenStore);//令牌存储策略
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
//这一句话,完成了在生成jwt令牌前添加信息
//配置扩展信息,一定要将自定义的TokenEnhancer放在前面,否则无效
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(myTokenEnhancer,accessTokenConverter));
service.setTokenEnhancer(tokenEnhancerChain);
service.setClientDetailsService(clientDetails());//设置客户端信息
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
}
}
额外发现:
在扩展jwt令牌信息的时候,我们选择TokenEnhancer来进行增加信息,但在看源码的过程中,发现在DefaultUserAuthenticationConverter
类中的 convertUserAuthentication
方法,这个方法就是将authentication转换为map**(map就是token中的键值对!!!)**
所以之前拓展jwt信息可以不使用TokenEnhancer,而是重写 convertUserAuthentication
方法,将authentication中的“user_info”添加到map中!!!然后就会根据map生成jwt令牌,并附带拓展信息。但是这样有一个弊端,就是你拓展信息不仅会出现在jwt令牌中,还会出现在token中。因为只要生成token就会调用convertUserAuthentication,所以生成jwt令牌时调用一次、生成token时又会调用一次。
如果你想让token的信息更丰富的话,倒是可以重写 convertUserAuthentication
。但是只想让jwt令牌信息更丰富,还是老老实实添加tokenEnhancer。
调用链:DefaultTokenServices的createAccessToken(生成一个uuid value)->TokenEnhancer->JwtAccessTokenConverter的enhance(将token中的value改为jwt令牌)->JwtAccessTokenConverter的encode(生成jwt令牌)->DefaultAccessTokenConverter(产生生成令牌的原料map)->DefaultUserAuthenticationConverter(从Authentication中读取map,username与AUTHORITIES就是在这添加进去的)
token键值对形式:
{
"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDE0OTQ1NzcsInVzZXJfbmFtZSI6IjEyMyIsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6ImViOWFlMWE5LTU2NDgtNGJmMC04NDZiLTlkM2QxZTJjNTVlNyIsImNsaWVudF9pZCI6IkhlYWx0aFBvcnRXZWJBcHAiLCJzY29wZSI6WyJhbGwiXX0.zqGJVy0LsoYysUmWgJCzYrOBsIDAmYn1UNM4R7S3w2g",
"refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiIxMjMiLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiZWI5YWUxYTktNTY0OC00YmYwLTg0NmItOWQzZDFlMmM1NWU3IiwiZXhwIjoxNzAxNjcxNzY0LCJhdXRob3JpdGllcyI6WyJwMSJdLCJqdGkiOiIxN2FjOGY1Mi1iYWRhLTRlYTctOGJmMi05MDllZGNmNWYzZTEiLCJjbGllbnRfaWQiOiJIZWFsdGhQb3J0V2ViQXBwIn0.oE-hfv-g1Xj4jpMCs7LIOUpxvZq57TFXQLPm-O7u-Tw",
"tokenHead": "Bearer ",
"expiresIn": 7199
}
DefaultUserAuthenticationConverter源码:
public class DefaultUserAuthenticationConverter implements UserAuthenticationConverter {
private Collection<? extends GrantedAuthority> defaultAuthorities;
private UserDetailsService userDetailsService;
//从authentication获取map作为生成token的原料
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
//这是refresh_token特有的方法,就是根据refresh_token中的信息生成Authentication
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Object principal = map.get(USERNAME);
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
if (userDetailsService != null) {
UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME));
authorities = user.getAuthorities();
principal = user;
}
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}
}