Spring-boot 资源服务器仅在过期时验证令牌

Posted

技术标签:

【中文标题】Spring-boot 资源服务器仅在过期时验证令牌【英文标题】:Spring-boot Resource server validate token only when expired 【发布时间】:2021-06-04 13:16:48 【问题描述】:

我正在使用spring-boot开发一个微服务结构,这个结构有一个外部的oauth2授权服务器和多个资源服务器。

我的问题是,对我的资源的每个 http 请求都会调用我的授权服务器的 url,以验证令牌 (.../oauth/check_token/)。这种方式有很多请求。有一种方法可以仅在过期时验证/检查此令牌?

我的资源服务器:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter 
    
    @Value("$security.oauth2.client.client-id")
    private String clientId;
    
    @Value("$security.oauth2.client.client-secret")
    private String clientSecret;
    
    @Value("$security.oauth2.resource.id")
    private String resourceId;
    
    @Value("$security.oauth2.resource.token-info-uri")
    private String tokenInfoUri;

    @Override
    public void configure(HttpSecurity http) throws Exception 
        http
            .authorizeRequests()
            .antMatchers(ADMIN_ANT_MATCHER).hasRole("ADMIN")
            .antMatchers(PROTECTED_ANT_MATCHER).hasRole("USER")
            .and()
            .csrf().disable();
    
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
         resources.tokenServices(tokenService()).resourceId(resourceId).stateless(true);
    
    
    @Bean
    @Primary
    public RemoteTokenServices tokenService() 
        RemoteTokenServices tokenService = new RemoteTokenServices();
        tokenService.setCheckTokenEndpointUrl(tokenInfoUri);
        tokenService.setClientId(clientId);
        tokenService.setClientSecret(clientSecret);
        return tokenService;
    

授权服务器:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 

    @Value("$security.oauth2.client.client-id")
    private String clientId;

    @Value("$security.oauth2.client.client-secret")
    private String clientSecret;

    @Value("$security.oauth2.resource.id")
    private String resourceId;

    @Value("$security.oauth2.client.access-token-validity-seconds")
    private Integer tokenValidateSeconds;

    @Value("$security.oauth2.client.token-secret")
    private String tokenSecret;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private OauthAccessTokenRepository oauthAccessTokenRepository;

    @Autowired
    private OauthRefreshTokenRepository oauthRefreshTokenRepository;


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints
                .tokenStore(this.tokenStore)
                .tokenEnhancer(tokenEnhancer())
                .authenticationManager(this.authenticationManager)
                .userDetailsService(userDetailsService);
    

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
        clients
                .inMemory()
                .withClient(clientId)
                .authorizedGrantTypes("client_credentials", "password", "refresh_token")
                .authorities("ROLE_USER","ROLE_ADMIN")
                .scopes("read","write","trust")
                .resourceIds(resourceId)
                .accessTokenValiditySeconds(tokenValidateSeconds)
                .secret(bCryptPasswordEncoder().encode(clientSecret));
    

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception 
        security.checkTokenAccess("isAuthenticated()");
    

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
        return new BCryptPasswordEncoder();
    

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() 
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setTokenStore(this.tokenStore);
        return tokenServices;
    

    @Bean
    public TokenEnhancer tokenEnhancer() 
        return new TokenEnhancer() 
            @Override
            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) 
                CustomUser user = (CustomUser) authentication.getPrincipal();

                String token = JwtTokenHelper.generateToken(
                        user.getIdUser(),
                        user.getTenantID(),
                        user.getIdEntity(),
                        user.getIdBusinessUnit(),
                        user.getProfile(),
                        tokenValidateSeconds,
                        tokenSecret
                );

                ((DefaultOAuth2AccessToken) accessToken).setValue(token);

                return accessToken;
            
        ;

    

【问题讨论】:

【参考方案1】:

您可以通过使用签名的JWT 令牌消除使用check_token endpoint 的需要。

当资源服务器收到 JWT 令牌时,它会使用公钥验证其签名,并通过检查 JSON 对象中的相应字段来验证其到期日期。

为此,您可以使用JwtAccessTokenConverter、JwtTokenStore 和nimbus-jose-jwt 库。

这种方法的缺点是您无法撤销令牌。那么最好有短暂的令牌。

【讨论】:

我确实按照你说的做,并且按预期工作,我也意识到性能应用比以前更好,谢谢你的帮助!

以上是关于Spring-boot 资源服务器仅在过期时验证令牌的主要内容,如果未能解决你的问题,请参考以下文章

仅在刷新页面时令牌过期时注销?为啥?

在服务总线后面验证过期的 JWT 令牌

Spring-Boot 仅在一个配置文件中执行 data.sql

身份验证和资源服务器之间的 OAuth v2 通信

spring-boot oauth2 拆分授权服务器和资源服务器

仅在验证成功时渲染组件