背景分析
-
1.客户端携带认证中心发放的token,请求资源服务器A(Spring Security OAuth 发放Token 源码解析)
-
2.客户端携带令牌直接访问资源服务器,资源服务器通过对token 的校验 (Spring Cloud OAuth2 资源服务器CheckToken 源码解析 ) 判断用户的合法性,并保存到上下文中
-
3.A服务接口接收到请求,需要通过Feign或者其他RPC框架调用B服务来组装返回数据
本文主要来探讨第三部 A –> B ,token 自定维护的源码实现
如何实现token 传递
配置feign 拦截器即可
-
此类是Feign 的拦截器实现
@Bean@ConditionalOnProperty(\"security.oauth2.client.client-id\")public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, OAuth2ProtectedResourceDetails resource,) { return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);}
源码解析
-
获取上下文中的token ,组装到请求头
-
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
-
// 给请求增加 token
-
@Override
-
public void apply(RequestTemplate template) {
-
template.header(header, extract(tokenType));
-
}
-
protected String extract(String tokenType) {
-
OAuth2AccessToken accessToken = getToken();
-
return String.format(\"%s %s\", tokenType, accessToken.getValue());
-
}
-
// 从spring security 上下文中获取token
-
public OAuth2AccessToken getToken() {
-
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
-
if (accessToken == null || accessToken.isExpired()) {
-
try {
-
accessToken = acquireAccessToken();
-
}
-
}
-
return accessToken;
-
}
-
}
-
再来看AccessTokenContextRelay, 上下文token 中转器.非常简单从上下文获取认证信息得到把 token 放到上下文
-
public class AccessTokenContextRelay {
-
private OAuth2ClientContext context;
-
public AccessTokenContextRelay(OAuth2ClientContext context) {
-
this.context = context;
-
}
-
public boolean copyToken() {
-
if (context.getAccessToken() == null) {
-
Authentication authentication = SecurityContextHolder.getContext()
-
.getAuthentication();
-
if (authentication != null) {
-
Object details = authentication.getDetails();
-
if (details instanceof OAuth2AuthenticationDetails) {
-
OAuth2AuthenticationDetails holder = (OAuth2AuthenticationDetails) details;
-
String token = holder.getTokenValue();
-
DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(
-
token);
-
String tokenType = holder.getTokenType();
-
if (tokenType != null) {
-
accessToken.setTokenType(tokenType);
-
}
-
context.setAccessToken(accessToken);
-
return true;
-
}
-
}
-
}
-
return false;
-
}
-
}
-
什么时候执行中转,oauth2 资源服务器非常简单暴力,加了个拦截器给转发。
源码非常简单
谈谈oauth 实现的问题
-
当请求上线文没有Token,如果调用feign 会直接,这个OAuth2FeignRequestInterceptor 肯定会报错,因为上下文copy 失败
-
如果设置线程隔离,这里也会报错。导致安全上下问题传递不到子线程中。
-
强制使用拦截器去处理 token 转发到这里上下文,使用的业务场景只有这里,影响性能高
这三个问题,大家在使用的过程中一定会遇到
自定义拦截器
-
通过外部条件是否执行token中转
-
public void apply(RequestTemplate template) {
-
Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);
-
if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {
-
return;
-
}
-
accessTokenContextRelay.copyToken();
-
if (oAuth2ClientContext != null
-
&& oAuth2ClientContext.getAccessToken() != null) {
-
super.apply(template);
-
}
-
}
-
手动调用accessTokenContextRelay的copy,当然需要覆盖原生oauth 客户端的配置
总结
-
以上源码参考个人项目 基于Spring Cloud、OAuth2.0开发基于Vue前后分离的开发平台
©著作权归作者所有:来自51CTO博客作者轮子工厂的原创作品,如需转载,请注明出处,否则将追究法律责任