【问题标题】:Spring Security OAuth2 revoke token does not workSpring Security OAuth2 撤销令牌不起作用
【发布时间】:2018-02-05 15:51:11
【问题描述】:

我的 Spring Boot 应用中有以下配置:

@Configuration
public class SecurityConfig {

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private BCryptPasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

@Configuration
@EnableAuthorizationServer
public static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    @Qualifier("myOauth2ClientDetailsService")
    private ClientDetailsService clientDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // set custom exception translator
        endpoints.exceptionTranslator(e -> {
            if (e instanceof OAuth2Exception) {
                OAuth2Exception exception = (OAuth2Exception) e;
                return ResponseEntity
                        .status(exception.getHttpErrorCode())
                        .body(new MyWLoginException(exception.getMessage()));
            } else {
                throw e;
            }
        });
        // other settings
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService).tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain);
    }

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

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new MyTokenEnhancer();
    }

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

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Order(-10)
public static class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.addFilterAfter(new AuditorFilter(), BasicAuthenticationFilter.class)
                .headers().frameOptions().disable()
             .and().csrf().disable()
             .authorizeRequests()
                .antMatchers("/img/**").permitAll()
                .anyRequest().authenticated();

    }

    @Override
    public void configure(ResourceServerSecurityConfigurer config) {
        config.tokenServices(tokenServices());
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        DefaultAccessTokenConverter defaultAccessTokenConverter = new DefaultAccessTokenConverter();
        defaultAccessTokenConverter.setUserTokenConverter(userAuthenticationConverter());
        converter.setAccessTokenConverter(defaultAccessTokenConverter);
        converter.setSigningKey("123");
        return converter;
    }

    @Bean
    public UserAuthenticationConverter userAuthenticationConverter() {
        return new MyUserAuthenticationConverter();
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }
}

}

另外,我有一个特殊的端点来撤销令牌,它通过以下方法处理请求:

@Autowired
private TokenStore tokenStore;
@Autowired
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
private ResourceServerTokenServices resourceServerTokenServices;
...
final String tokenValue = ((OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails()).getTokenValue();
        final OAuth2AccessToken token = tokenStore.readAccessToken(tokenValue);
        tokenStore.removeAccessToken(token);
        boolean authRemoved = ((DefaultTokenServices) authorizationServerTokenServices).revokeToken(tokenValue); // <- true
        boolean resourceRemoved = ((DefaultTokenServices) resourceServerTokenServices).revokeToken(tokenValue); // <- true
        SecurityContextHolder.getContext().setAuthentication(null);

没有任何错误。我看到令牌服务返回 true(已删除)。但是当我用旧的访问令牌调用任何端点时,它就像这个令牌仍然存在一样工作。但是我从身份验证服务器和资源服务器中删除了令牌。如何解决这个问题?

【问题讨论】:

    标签: java spring spring-security spring-security-oauth2


    【解决方案1】:

    令牌撤销无法使用 JWT 进行,因为它嵌入其中的令牌已过期。您的授权服务器在颁发后不会捕获有关令牌的任何信息。因此,也许您应该尝试在您的授权服务器中使用JdbcTokenStore 将您的令牌保存在数据库中,然后根据需要撤销它们(或者也可以在内存中)。如果您的应用是分开的,您可以使用RemoteTokenServices 来验证您的令牌。

    Here 是一个向您展示如何完成此任务的教程。

    【讨论】:

    • 我猜你是对的。我打算使用 RedisTokenStore。我认为我需要单一且清晰的令牌存储
    猜你喜欢
    • 2017-11-09
    • 2017-10-03
    • 2015-12-13
    • 2021-12-27
    • 2020-10-19
    • 2020-07-31
    • 1970-01-01
    • 1970-01-01
    • 2020-07-20
    相关资源
    最近更新 更多