本文不详述如何创建 JWT,oauth 2.0 服务端可以参考文章: Spring Security oauth2.0 服务端
JWT 添加额外信息
oauth 2.0 JWT 默认返回 OAuth2AccessToken 接口的实现类,默认实现类是 DefaultOAuth2AccessToken,返回字段有 5 个
- access_token:表示访问令牌,必选项
- token_type:表示令牌类型,该值大小写不敏感,必选项,默认是 bearer 类型
- expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
- refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
- scope:表示权限范围,如果与客户端申请的范围一致,此处可省略
TokenEhancer
往 JWT 添加额外信息,首先要实现 TokenEhancer(令牌增强器)接口
- DefaultOAuth2AccessToken:默认的 token 实现类
- setAdditionalInformation:添加其他信息,参数类型是 Map<String, Object>
@Bean
public TokenEnhancer customTokenEnhancer() {
return (accessToken, authentication) -> {
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("fat", "test");
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
};
}
AuthorizationServerEndpointsConfigurer
把 TokenEnhancer 添加到 AuthorizationServerConfigurerAdapter 里
- 创建 TokenEnhancerChain,传参类型为 list
- 创建 DefaultTokenServices,传入 TokenEnhancerChain
- endpoints 传入 DefaultTokenServices
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
...
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), customTokenEnhancer()));
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenEnhancer(enhancerChain);
tokenServices.setTokenStore(tokenStore());
endpoints
.authenticationManager(authenticationManager)
.tokenServices(tokenServices)
;
}
...
}
访问 /oauth/token 接口时,返回值返回插入的 fat 字段
JWT 解析发现没有 fat 字段
这里有一个值得注意的地方了,若要 JWT 解析时有新增字段,在 setTokenEnhancers 时,参数 list 需要把自定义的 customTokenEnhancer() 放前面
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), jwtAccessTokenConverter()));
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenEnhancer(enhancerChain);
tokenServices.setTokenStore(tokenStore());
endpoints
.authenticationManager(authenticationManager)
.tokenServices(tokenServices)
;
}
check/token 添加额外信息
自定义 AccessTokenConverter
- 继承 DefaultAccessTokenConverter
- 重新 convertAccessToken 方法,添加需要新增的字段
@Configuration
public class AuthAccessTokenConverter extends DefaultAccessTokenConverter {
@Override
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
Map<String, Object> response = (Map<String, Object>) super.convertAccessToken(token, authentication);
response.put("feng","myfat");
return response;
}
}
把新建的 AccessTokenConverter 放进配置里
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthAccessTokenConverter converter;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
...
endpoints.accessTokenConverter(converter);
}
}
每次返回新 token
默认情况下,通过相同的参数请求 /oauth/token 接口,返回的 access_token 是同一个 token,而 expires_in 会减少,expires_in 是 token 的有效期,单位为秒
很多场景,都需要我们每次请求都返回一个新的 token,那么如何实现呢,下面先了解一下 DefaultAuthenticationKeyGenerator
- 默认的身份验证密钥生成器
- 根据 client id、scope、username 生成
- authentication.isClientOnly():判断 grant_type 是否 client_credentials 模式
public String extractKey(OAuth2Authentication authentication) {
Map<String, String> values = new LinkedHashMap<String, String>();
OAuth2Request authorizationRequest = authentication.getOAuth2Request();
if (!authentication.isClientOnly()) {
values.put(USERNAME, authentication.getName());
}
values.put(CLIENT_ID, authorizationRequest.getClientId());
if (authorizationRequest.getScope() != null) {
values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
}
return generateKey(values);
}
很容易看出,只要 values 不相同,那么生成的 token 就不相同
那么思路来了
- 新增 AuthenticationKeyGenerator 继承 DefaultAuthenticationKeyGenerator
- 重新 extractKey() 方法
- values 添加永远不重复的 CODE
- 把 AuthenticationKeyGenerator 添加到 TokenStore
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
...
private class AuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
private static final String CLIENT_ID = "client_id";
private static final String SCOPE = "scope";
private static final String USERNAME = "username";
private static final String CODE = "code";
@Override
public String extractKey(OAuth2Authentication authentication) {
Map<String, String> values = new LinkedHashMap<String, String>();
OAuth2Request authorizationRequest = authentication.getOAuth2Request();
if (!authentication.isClientOnly()) {
values.put(USERNAME, authentication.getName());
}
values.put(CLIENT_ID, authorizationRequest.getClientId());
if (authorizationRequest.getScope() != null) {
values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
}
UUID uuid4 = UUID.randomUUID();
values.put(CODE, uuid4.toString());
return generateKey(values);
}
}
@Bean
public TokenStore tokenStore() {
InMemoryTokenStore tokenStore = new InMemoryTokenStore();
tokenStore.setAuthenticationKeyGenerator(new AuthenticationKeyGenerator());
return tokenStore;
}
...
}
本文含有隐藏内容,请 开通VIP 后查看