来自 技术 2019-04-17 00:00 的文章

Spring Cloud OAuth 实现微服务内部Token传递的源码解

八年Java开发的感悟:什么才是程序员的立身之本>>>   

背景分析

1.客户端携带认证中心发放的token,请求资源服务器A(Spring Security OAuth 发放Token 源码解析)

2.客户端携带令牌直接访问资源服务器,资源服务器通过对token 的校验 (Spring Cloud OAuth2 资源服务器CheckToken 源码解析) 判断用户的合法性,并保存到上下文中

3.A服务接口接收到请求,需要通过Feign或者其他RPC框架调用B服务来组装返回数据

本文主要来探讨第三部 A --> B ,token 自定维护的源码实现

如何实现token 传递配置OAuth2FeignRequestInterceptor 即可此类是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@Overridepublic 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 上下文中获取tokenpublic 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 资源服务器非常简单暴力,加了个拦截器给转发。

源码非常简单

谈谈spring security oauth 实现的问题当请求上线文没有Token,如果调用feign 会直接,这个OAuth2FeignRequestInterceptor 肯定会报错,因为上下文copy 失败如果设置线程隔离,这里也会报错。导致安全上下问题传递不到子线程中。强制使用拦截器去处理 token 转发到这里上下文,使用的业务场景只有这里,影响性能高

这三个问题,大家在使用的过程中一定会遇到

自定义OAuth2FeignRequestInterceptor通过外部条件是否执行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前后分离的开发平台QQ: 2270033969 一起来聊聊你们是咋用 spring cloud 的吧。

欢迎关注我们获得更多的好玩JavaEE 实践