扩展资源服务器解决oauth2 性能瓶颈

3次阅读

共计 3419 个字符,预计需要花费 9 分钟才能阅读完成。

用户携带 token 请求资源服务器
资源服务器拦截器 携带 token 去认证服务器 调用 tokenstore 对 token 合法性校验
资源服务器拿到 token,默认只会含有用户名信息
通过用户名调用 userdetailsservice.loadbyusername 查询用户全部信息

详细性能瓶颈分析,请参考上篇文章《扩展 jwt 解决 oauth2 性能瓶颈》本文是针对传统使用 UUID token 的情况进行扩展,提高系统的吞吐率,解决性能瓶颈的问题
默认 check-token 解析逻辑
RemoteTokenServices 入口
@Override
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {

MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
formData.add(tokenName, accessToken);
HttpHeaders headers = new HttpHeaders();
headers.set(“Authorization”, getAuthorizationHeader(clientId, clientSecret));
// 调用认证服务器的 check-token 接口检查 token
Map<String, Object> map = postForMap(checkTokenEndpointUrl, formData, headers);

return tokenConverter.extractAuthentication(map);
}
解析认证服务器返回的信息
DefaultAccessTokenConverter
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
Map<String, String> parameters = new HashMap<String, String>();
Set<String> scope = extractScope(map);
// 主要是 用户的信息的抽取
Authentication user = userTokenConverter.extractAuthentication(map);
// 一些 oauth2 信息的填充
OAuth2Request request = new OAuth2Request(parameters, clientId, authorities, true, scope, resourceIds, null, null,
null);
return new OAuth2Authentication(request, user);
}
组装当前用户信息
DefaultUserAuthenticationConverter
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;
}
问题分析

认证服务器 check-token 返回的全部信息
资源服务器在根据返回信息组装用户信息的时候,只是用了 username
如果设置了 userDetailsService 的实现则去调用 loadUserByUsername 再去查询一次用户信息

造成问题现象

如果设置了 userDetailsService 即可在 spring security 上下文获取用户的全部信息,不设置则只能得到用户名。
增加了一次查询逻辑,对性能产生不必要的影响

解决问题
扩展 UserAuthenticationConverter 的解析过程,把认证服务器返回的信息全部组装到 spring security 的上下文对象中
/**
* @author lengleng
* @date 2019-03-07
* <p>
* 根据 checktoken 的结果转化用户信息
*/
public class PigxUserAuthenticationConverter implements UserAuthenticationConverter {
private static final String N_A = “N/A”;
// map 是 check-token 返回的全部信息
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);

String username = (String) map.get(USERNAME);
Integer id = (Integer) map.get(SecurityConstants.DETAILS_USER_ID);
Integer deptId = (Integer) map.get(SecurityConstants.DETAILS_DEPT_ID);
Integer tenantId = (Integer) map.get(SecurityConstants.DETAILS_TENANT_ID);
PigxUser user = new PigxUser(id, deptId, tenantId, username, N_A, true
, true, true, true, authorities);
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
}
return null;
}
}
给 remoteTokenServices 注入这个实现
public class PigxResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {

@Override
public void configure(ResourceServerSecurityConfigurer resources) {
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
UserAuthenticationConverter userTokenConverter = new PigxUserAuthenticationConverter();
accessTokenConverter.setUserTokenConverter(userTokenConverter);

remoteTokenServices.setRestTemplate(lbRestTemplate);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
resources.
.tokenServices(remoteTokenServices);
}
}

完成扩展,再来看文章开头的流程图就变成了如下

关注我

个人项目 基于 Spring Cloud、OAuth2.0 开发基于 Vue 前后分离的开发平台

QQ: 2270033969 一起来聊聊你们是咋用 spring cloud 的吧。

正文完
 0