当前位置: 首页 > news >正文

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

  • 用户携带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 再去查询一次用户信息

造成问题现象

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

解决问题

  • 扩展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 的吧。

相关文章:

  • 云端服务器与传统的独立主机服务器有什么区别
  • 中台之上(十三):探讨支持组装式开发的业务架构设计方法
  • 阿里CTO:阿里所有技术和产品输出都将必须通过阿里云进行
  • python -迭代器与生成器 以及 iterable(可迭代对象)、yield语句
  • LeetCode.914 卡牌分组
  • 本周学习小结(25/03 - 31/03)
  • Beetl 提供俩种方式来显示实现局部更新
  • core_framework —— 基于libev的轻量级lua网络开发框架
  • MyCAT水平分库
  • 织梦CMS模板中dede标签使用php和if判断语句的方法
  • 2019第十四届中国竞争情报国际年会将于4月在上海召开
  • 移动互联网下半场,iOS开发者如何“高薪”成长?
  • 判断是手机端还是PC端的代码
  • linux分区方案
  • 邮件加密之SMIME
  • CODING 缺陷管理功能正式开始公测
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • Java读取Properties文件的六种方法
  • Linux Process Manage
  • linux学习笔记
  • ViewService——一种保证客户端与服务端同步的方法
  • 关于List、List?、ListObject的区别
  • 记一次和乔布斯合作最难忘的经历
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 设计模式走一遍---观察者模式
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 通过npm或yarn自动生成vue组件
  • 测评:对于写作的人来说,Markdown是你最好的朋友 ...
  • 如何用纯 CSS 创作一个货车 loader
  • #ifdef 的技巧用法
  • #每日一题合集#牛客JZ23-JZ33
  • (03)光刻——半导体电路的绘制
  • (1)虚拟机的安装与使用,linux系统安装
  • (13):Silverlight 2 数据与通信之WebRequest
  • (22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)linux下的时间函数使用
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .Net下使用 Geb.Video.FFMPEG 操作视频文件
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • [ 渗透工具篇 ] 一篇文章让你掌握神奇的shuize -- 信息收集自动化工具
  • [android] 请求码和结果码的作用
  • [C#]DataTable常用操作总结【转】
  • [C/C++]关于C++11中的std::move和std::forward
  • [c++] 什么是平凡类型,标准布局类型,POD类型,聚合体
  • [CQOI 2011]动态逆序对
  • [CTO札记]盛大文学公司名称对联
  • [Deep Learning] 神经网络基础