扩展jwt解决oauth2 性能瓶颈

简介: oauth2 性能瓶颈 资源服务器的请求都会被拦截 到认证服务器校验合法性 (如下图) 用户携带token 请求资源服务器 资源服务器拦截器 携带token 去认证服务器 调用tokenstore 对token 合法性校验 资源服务器拿到token,默认只会含有用户名信息 通过用户名调用userdetailsservice.loadbyusername 查询用户全部信息 如上步骤在实际使用,会造成认证中心的负载压力过大,成为造成整个系统瓶颈的关键点。

oauth2 性能瓶颈

资源服务器的请求都会被拦截 到认证服务器校验合法性 (如下图)

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

如上步骤在实际使用,会造成认证中心的负载压力过大,成为造成整个系统瓶颈的关键点。

check-token 过程中涉及的源码

扩展jwt 生成携带用户详细信息

  • 为什么使用jwt 替代默认的UUID token ?
    通过jwt 访问资源服务器后,不再使用check-token 过程,通过对jwt 的解析即可实现身份验证,登录信息的传递。减少网络开销,提高整体微服务集群的性能
  • spring security oauth 默认的jwttoken 只含有username,通过扩展TokenEnhancer,实现关键字段的注入到 JWT 中,方便资源服务器使用
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            if (SecurityConstants.CLIENT_CREDENTIALS
                    .equals(authentication.getOAuth2Request().getGrantType())) {
                return accessToken;
            }

            final Map<String, Object> additionalInfo = new HashMap<>(8);
            PigxUser pigxUser = (PigxUser) authentication.getUserAuthentication().getPrincipal();
            additionalInfo.put("user_id", pigxUser.getId());
            additionalInfo.put("username", pigxUser.getUsername());
            additionalInfo.put("dept_id", pigxUser.getDeptId());
            additionalInfo.put("tenant_id", pigxUser.getTenantId());
            additionalInfo.put("license", SecurityConstants.PIGX_LICENSE);
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
            return accessToken;
        };
    }
  • 生成的token 如下,含有关键的字段

重写默认的资源服务器处理行为

  • 不再使用RemoteTokenServices 去掉用认证中心 CheckToken,自定义客户端TokenService
@Slf4j
public class PigxCustomTokenServices implements ResourceServerTokenServices {
    @Setter
    private TokenStore tokenStore;

    @Setter
    private DefaultAccessTokenConverter defaultAccessTokenConverter;

    @Setter
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    
    @Override
    public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
        OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
        UserAuthenticationConverter userTokenConverter = new PigxUserAuthenticationConverter();
        defaultAccessTokenConverter.setUserTokenConverter(userTokenConverter);
        Map<String, ?> map = jwtAccessTokenConverter.convertAccessToken(readAccessToken(accessToken), oAuth2Authentication);
        return defaultAccessTokenConverter.extractAuthentication(map);
    }


    @Override
    public OAuth2AccessToken readAccessToken(String accessToken) {
        return tokenStore.readAccessToken(accessToken);
    }
}
  • 解析jwt 组装成Authentication
/**
 * @author lengleng
 * @date 2019-03-17
 * <p>
 * jwt 转化用户信息
 */
public class PigxUserAuthenticationConverter implements UserAuthenticationConverter {
    private static final String USER_ID = "user_id";
    private static final String DEPT_ID = "dept_id";
    private static final String TENANT_ID = "tenant_id";
    private static final String N_A = "N/A";
    
    @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(USER_ID);
            Integer deptId = (Integer) map.get(DEPT_ID);
            Integer tenantId = (Integer) map.get(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;
    }

    private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
        Object authorities = map.get(AUTHORITIES);
        if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                    .collectionToCommaDelimitedString((Collection<?>) authorities));
        }
        throw new IllegalArgumentException("Authorities must be either a String or a Collection");
    }
}
  • 资源服务器配置中注入以上配置即可
@Slf4j
public class PigxResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        UserAuthenticationConverter userTokenConverter = new PigxUserAuthenticationConverter();
        accessTokenConverter.setUserTokenConverter(userTokenConverter);

        PigxCustomTokenServices tokenServices = new PigxCustomTokenServices();
        
        // 这里的签名key 保持和认证中心一致
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        converter.setVerifier(new MacSigner("123"));
        JwtTokenStore jwtTokenStore = new JwtTokenStore(converter);
        tokenServices.setTokenStore(jwtTokenStore);
        tokenServices.setJwtAccessTokenConverter(converter);
        tokenServices.setDefaultAccessTokenConverter(accessTokenConverter);

        resources
                .authenticationEntryPoint(resourceAuthExceptionEntryPoint)
                .tokenServices(tokenServices);
    }
}

使用JWT 扩展后带来的问题

  • JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
  • 去认证服务器校验的过程就是 通过tokenstore 来控制jwt 安全性的一个方法,去掉Check-token 意味着 jwt token 安全性不可保证
  • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
  • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

关注我

目录
相关文章
|
9月前
|
缓存 安全 NoSQL
Spring Cloud实战 | 第六篇:Spring Cloud Gateway+ Spring Security OAuth2 + JWT实现微服务统一认证鉴权
Spring Cloud实战 | 第六篇:Spring Cloud Gateway+ Spring Security OAuth2 + JWT实现微服务统一认证鉴权
Spring Cloud实战 | 第六篇:Spring Cloud Gateway+ Spring Security OAuth2 + JWT实现微服务统一认证鉴权
|
SpringCloudAlibaba NoSQL 安全
SpringCloudAlibaba篇(九)SpringCloudGateWay整合Oauth2+Jwt实现认证中心
SpringCloudAlibaba篇(九)SpringCloudGateWay整合Oauth2+Jwt实现认证中心
2156 0
SpringCloudAlibaba篇(九)SpringCloudGateWay整合Oauth2+Jwt实现认证中心
|
4月前
|
安全 Java API
深度解析 Spring Security:身份验证、授权、OAuth2 和 JWT 身份验证的完整指南
Spring Security 是一个用于保护基于 Java 的应用程序的框架。它是一个功能强大且高度可定制的身份验证和访问控制框架,可以轻松地集成到各种应用程序中,包括 Web 应用程序和 RESTful Web 服务。 Spring Security 提供了全面的安全解决方案,用于身份验证和授权,并且可以用于在 Web 和方法级别上保护应用程序。
272 0
|
7月前
|
存储 JSON 安全
JWT验证用户信息功能与OAuth2协议
JWT验证用户信息功能与OAuth2协议
72 0
|
9月前
|
存储 安全 算法
十七.SpringCloud+Security+Oauth2实现微服务授权 -非对称加密生成JWT令牌
SpringCloud+Security+Oauth2实现微服务授权 -非对称加密生成JWT令牌
|
9月前
|
存储 JSON 缓存
九.SpringCloud+Security+Oauth2实现微服务授权 - Oauth2&JWT的认识
SpringCloud+Security+Oauth2实现微服务授权 - Oauth2&JWT的认识
|
存储 JSON NoSQL
浅析单点登录(重点讲解OAuth2+JWT)
浅析单点登录(重点讲解OAuth2+JWT)
512 0
浅析单点登录(重点讲解OAuth2+JWT)
|
存储 JSON NoSQL
16、Spring Security Oauth2 JWT(二)
用户身份认证:用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问。常见的用户身份认证表现形式有:用户名密码登录,指纹打卡等方式。
273 0
16、Spring Security Oauth2 JWT(二)
|
存储 人工智能 安全
16、Spring Security Oauth2 JWT(一)
用户身份认证:用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问。常见的用户身份认证表现形式有:用户名密码登录,指纹打卡等方式。
219 0
16、Spring Security Oauth2 JWT(一)
|
消息中间件 监控 Cloud Native
基于SpringCloud体系实现的一套支持云原生的分布式微服务架构,提供OAuth2/JWT权限认证、分布式事务、灰度、限流、链路追踪等功能,支持Docker容器化部署、镜像交付、K8S容器编排
lion是基于Spring Cloud体系实现的一套支持云原生的分布式微服务架构,为了让中小型公司解决当下技术瓶颈,快速将现有应用服务架构拆分改造为分布式微服务架构,进入 All-in-Cloud 时代,只需在本架构上进行相关业务开发即可,大大减少了分布式微服务架构的门槛,仅在本框架上做&quot;减法&quot;的目的,使架构师及开发人员不必过多的关注架构本身,只需专注于业务开发
基于SpringCloud体系实现的一套支持云原生的分布式微服务架构,提供OAuth2/JWT权限认证、分布式事务、灰度、限流、链路追踪等功能,支持Docker容器化部署、镜像交付、K8S容器编排