OAuth2.0 - 使用JWT替换Token 及 JWT内容增强

Posted 小毕超

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OAuth2.0 - 使用JWT替换Token 及 JWT内容增强相关的知识,希望对你有一定的参考价值。

一、OAuth2.0

上篇文章我们讲解了OAuth2.0的几种认证模式,前面的讲解Token采用OAuth2.0自带的方式生成的Token,但是这种方式也存在这弊端,通过前面的测试我们发现,当资源服务和授权服务不在一起时资源服务使用RemoteTokenServices 远程请求授权服务验证token,如果访问量较大将会影响系统的性能 。

因此我们可以采用JWT生成令牌,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务完成授权。

下面是上篇文章的地址:

https://blog.csdn.net/qq_43692950/article/details/122523524

二、OAuth2.0整合JWT

本篇文章接着上篇文章的内容继续修改。

认证服务修改

先声明JWT的签名模式,这里采用对称加密的方式,密钥设为bxc123,这个在资源服务器也是这么修改:

@Configuration
public class TokenConfig 

    private String SIGNING_KEY = "bxc123";

    @Bean

    public TokenStore tokenStore() 
        return new JwtTokenStore(accessTokenConverter());

    

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() 
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    

下面还要修改AuthorizationServer配制类,主要AuthorizationServerTokenServices的配制,增加一个TokenEnhancerChain,将上面声明出的JwtAccessTokenConverter 配制进去。

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter 

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
        clients.inMemory()// 使用in‐memory存储
                .withClient("c1")// client_id
                .secret(new BCryptPasswordEncoder().encode("secret"))
                .resourceIds("res1")
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")// 该client允许的授权类型 authorization_code,password,refresh_token,implicit,client_credentials
                .scopes("all")// 允许的授权范围
                .autoApprove(false) //加上验证回调地址
                .authorities("admin")
                .redirectUris("http://www.baidu.com");
    

    //设置授权码模式的授权码如何存取,暂时采用内存方式
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() 
        return new InMemoryAuthorizationCodeServices();
    

    @Bean
    public AuthorizationServerTokenServices tokenService() 
        DefaultTokenServices service = new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);
        service.setSupportRefreshToken(true);
        service.setTokenStore(tokenStore);
        //令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
        tokenEnhancers.add(accessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);

        service.setTokenEnhancer(tokenEnhancerChain);
        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) 
        endpoints
                .authenticationManager(authenticationManager)//认证管理器
                .authorizationCodeServices(authorizationCodeServices)//授权码服务
                .tokenServices(tokenService())//令牌管理服务
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception 
        security
                .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
                .checkTokenAccess("permitAll()")                  //oauth/check_token公开
                .allowFormAuthenticationForClients();                //表单认证(申请令牌)
    

重启auth认证服务。

资源服务修改

将上面写的TokenConfig类,覆盖到资源服务中,并修改ResouceServerConfig配制类,将前面写的远程访问认证服务校验令牌的逻辑,就可以去除掉了:

@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter 
    public static final String RESOURCE_ID = "res1";

    @Autowired
    TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) 
        resources.resourceId(RESOURCE_ID)//资源 id
                .tokenStore(tokenStore)
                .stateless(true);
    

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http
                .authorizeRequests()
                .antMatchers("/admin/**").hasAuthority("admin")
                .antMatchers("/common/**").hasAuthority("common")
                .and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    

重启资源服务。

三、测试

使用密码模式登录系统获取token,可以看到获取的token已经是Jwt格式的了:

可以在一些Jwt在线解析的网站,解析下JWT:

下面使用token去访问资源服务接口:

四、JWT内容增强

上面可以看到JWT中默认放置了一些用户的信息,如果我们想要存放其他东西呢,只需使用DefaultOAuth2AccessTokensetAdditionalInformation方法,传递一个Map即可放置自定义的数据,其中可以获取用户的信息,也可以再查找其他自定义的信息放置,下面修改认证服务的AuthorizationServer配制类:

添加TokenEnhancer tokenEnhancer()方法:

 /**
  * JWT内容增强
  */
 @Bean
 public TokenEnhancer tokenEnhancer() 
     return (accessToken, authentication) -> 
         Map<String, Object> additionalInfo = new HashMap<>();
         User principal = (User)authentication.getUserAuthentication().getPrincipal();
         String username = principal.getUsername();
         additionalInfo.put("three","额外的内容!");
         ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
         return accessToken;
     ;
 

在构建TokenEnhancerChain对象时,将上面配制的设置进去:

@Bean
public AuthorizationServerTokenServices tokenService() 
    DefaultTokenServices service = new DefaultTokenServices();
    service.setClientDetailsService(clientDetailsService);
    service.setSupportRefreshToken(true);
    service.setTokenStore(tokenStore);
    //令牌增强
    TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
    //内容增强
    tokenEnhancers.add(tokenEnhancer());
    tokenEnhancers.add(accessTokenConverter);
    tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);

    service.setTokenEnhancer(tokenEnhancerChain);
    service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
    service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
    return service;

重启认证服务,再次登录,就可以看到增加的信息已经有了:

如果解析JWT,也可以看到内容:


喜欢的小伙伴可以关注我的个人微信公众号,获取更多学习资料!

以上是关于OAuth2.0 - 使用JWT替换Token 及 JWT内容增强的主要内容,如果未能解决你的问题,请参考以下文章

OAuth2.0 - 介绍与使用 及 授权码模式讲解

OAuth2.0-JWT令牌

OAuth 2.0 范围参数与 OAuth 2.0 JWT access_token 范围声明

OAuth 2.0 与 JWT

OpenID Connect Core 1.0ID Token

博客园OAuth2.0 授权及用户登录