您的位置:首页 > 编程语言 > Java开发

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

2021-03-03 22:03 1196 查看

背景分析

  • 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 ,组装到请求头

  1. public class OAuth2FeignRequestInterceptor implements RequestInterceptor {

  2.    // 给请求增加 token

  3.    @Override

  4.    public void apply(RequestTemplate template) {

  5.        template.header(header, extract(tokenType));

  6.    }


  7.    protected String extract(String tokenType) {

  8.        OAuth2AccessToken accessToken = getToken();

  9.        return String.format("%s %s", tokenType, accessToken.getValue());

  10.    }


  11.    // 从spring security 上下文中获取token

  12.    public OAuth2AccessToken getToken() {


  13.        OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();

  14.        if (accessToken == null || accessToken.isExpired()) {

  15.            try {

  16.                accessToken = acquireAccessToken();

  17.            }

  18.        }

  19.        return accessToken;

  20.    }


  21. }

  • 再来看AccessTokenContextRelay, 上下文token 中转器.非常简单从上下文获取认证信息得到把 token 放到上下文

  1. public class AccessTokenContextRelay {


  2.    private OAuth2ClientContext context;


  3.    public AccessTokenContextRelay(OAuth2ClientContext context) {

  4.        this.context = context;

  5.    }


  6.    public boolean copyToken() {

  7.        if (context.getAccessToken() == null) {

  8.            Authentication authentication = SecurityContextHolder.getContext()

  9.                    .getAuthentication();

  10.            if (authentication != null) {

  11.                Object details = authentication.getDetails();

  12.                if (details instanceof OAuth2AuthenticationDetails) {

  13.                    OAuth2AuthenticationDetails holder = (OAuth2AuthenticationDetails) details;

  14.                    String token = holder.getTokenValue();

  15.                    DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(

  16.                            token);

  17.                    String tokenType = holder.getTokenType();

  18.                    if (tokenType != null) {

  19.                        accessToken.setTokenType(tokenType);

  20.                    }

  21.                    context.setAccessToken(accessToken);

  22.                    return true;

  23.                }

  24.            }

  25.        }

  26.        return false;

  27.    }


  28. }

  • 什么时候执行中转,oauth2 资源服务器非常简单暴力,加了个拦截器给转发。

源码非常简单

谈谈oauth 实现的问题

  1. 当请求上线文没有Token,如果调用feign 会直接,这个OAuth2FeignRequestInterceptor 肯定会报错,因为上下文copy 失败

  2. 如果设置线程隔离,这里也会报错。导致安全上下问题传递不到子线程中。

  3. 强制使用拦截器去处理 token 转发到这里上下文,使用的业务场景只有这里,影响性能高

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

自定义拦截器

  • 通过外部条件是否执行token中转

  1. public void apply(RequestTemplate template) {

  2.    Collection<String> fromHeader = template.headers().get(SecurityConstants.FROM);

  3.    if (CollUtil.isNotEmpty(fromHeader) && fromHeader.contains(SecurityConstants.FROM_IN)) {

  4.        return;

  5.    }


  6.    accessTokenContextRelay.copyToken();

  7.    if (oAuth2ClientContext != null

  8.        && oAuth2ClientContext.getAccessToken() != null) {

  9.        super.apply(template);

  10.    }

  11. }

  • 手动调用accessTokenContextRelay的copy,当然需要覆盖原生oauth 客户端的配置 

总结

  • 以上源码参考个人项目 基于Spring Cloud、OAuth2.0开发基于Vue前后分离的开发平台


©著作权归作者所有:来自51CTO博客作者轮子工厂的原创作品,如需转载,请注明出处,否则将追究法律责任
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: