自定义Spring Authorization Server OAuth2令牌撤销端点时无法捕获处理OAuth2AuthenticationException
我正在尝试编写自定义的OAuth2令牌撤销端点,但遇到了一个棘手的问题:
我想在自定义的.errorResponseHandler(errorResponseHandler)中捕获并处理authenticationProvider抛出的OAuth2AuthenticationException,但无论怎么尝试都无法触发这个处理逻辑。之后我还换了思路,创建了实现AuthenticationEntryPoint的CustomAuthenticationEntryPoint并把它加入到filterChain里,结果还是不行。
下面是我的相关代码类:
filterChain
@Configuration(proxyBeanMethods = false) @EnableWebSecurity @Profile("dev") public class AuthServerConfigDev extends AuthServerConfigAbstract{ private static final Logger logger = LoggerFactory.getLogger(AuthServerConfigDev.class); private final OAuth2AuthorizationService authorizationService; //private final TokenRevocationRepository tokenRevocationRepository; public AuthServerConfigDev(@Lazy OAuth2AuthorizationService authorizationService/*, TokenRevocationRepository tokenRevocationRepository*/) { this.authorizationService = authorizationService; // this.tokenRevocationRepository = tokenRevocationRepository; } @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfigurer authzServerConfigurer = new OAuth2AuthorizationServerConfigurer(); authzServerConfigurer .oidc(withDefaults()); authzServerConfigurer .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint .errorResponseHandler(new ClientAuthenticationFailureHandler())); authzServerConfigurer .tokenEndpoint(tokenEndpoint -> tokenEndpoint .errorResponseHandler(new ClientAuthenticationFailureHandler())); RequestMatcher endpointsMatcher = authzServerConfigurer.getEndpointsMatcher(); http.addFilterBefore(new GrantFlowFilter(new ClientAuthenticationFailureHandler()), UsernamePasswordAuthenticationFilter.class); http .securityMatchers(matchers -> matchers .requestMatchers( antMatcher(OPENAPI_JSON_URL), antMatcher(SWAGGER_UI_URL), antMatcher(REST_USER_PATH + "/**"), antMatcher(REVOKE_ENDPOINT), endpointsMatcher)) .authorizeHttpRequests(authorize -> authorize .requestMatchers(HttpMethod.GET, OPENAPI_JSON_URL).hasAuthority("SCOPE.user") .requestMatchers(HttpMethod.GET, SWAGGER_UI_URL).hasAuthority("SCOPE.user") .requestMatchers(FULL_LOGIN_URL).permitAll() .requestMatchers(REVOKE_ENDPOINT).authenticated() .anyRequest().authenticated()) .csrf(csrf -> csrf .ignoringRequestMatchers(endpointsMatcher)) .exceptionHandling(exceptions -> exceptions .defaultAuthenticationEntryPointFor( new LoginUrlAuthenticationEntryPoint(FULL_LOGIN_URL + ParamMissingAuth), new MediaTypeRequestMatcher(MediaType.TEXT_HTML))) .oauth2ResourceServer(resourceServer -> resourceServer.jwt(withDefaults())) .with(authzServerConfigurer, (authorizationServer) -> authorizationServer // 按照指南定义 .tokenRevocationEndpoint(tokenRevocationEndpoint -> tokenRevocationEndpoint .revocationRequestConverter(new CustomRevocationRequestConverter()) .authenticationProvider(new CustomRevocationAuthenticationProvider(authorizationService/*, tokenRevocationRepository*/)) .revocationResponseHandler(new CustomRevocationResponseHandler()) .errorResponseHandler(new CustomRevocationErrorResponseHandler()))); return http.build(); } }
CustomRevocationRequestConverter
public class CustomRevocationRequestConverter implements AuthenticationConverter { @Override public Authentication convert(HttpServletRequest request) { // 从请求参数中提取token和token_type_hint String tokenValue = request.getParameter("token"); String tokenTypeHint = request.getParameter("token_type_hint"); if (tokenValue == null || tokenValue.isEmpty()) { return null; // 如果token不存在,返回null } // 从SecurityContext中获取客户端认证信息 Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication(); // 创建并返回OAuth2TokenRevocationAuthenticationToken if(tokenTypeHint != null && !tokenTypeHint.isEmpty()) return new OAuth2TokenRevocationAuthenticationToken(tokenValue, clientPrincipal, tokenTypeHint); else { return new OAuth2TokenRevocationAuthenticationToken(tokenValue, clientPrincipal, null); } } }
CustomRevocationAuthenticationProvider
public class CustomRevocationAuthenticationProvider implements AuthenticationProvider { private static final Logger logger = LoggerFactory.getLogger(CustomRevocationAuthenticationProvider.class); private final OAuth2AuthorizationService authorizationService; // private final TokenRevocationRepository tokenRevocationRepository; public CustomRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService/*, TokenRevocationRepository tokenRevocationRepository*/) { this.authorizationService = authorizationService; // this.tokenRevocationRepository = tokenRevocationRepository; } @Transactional @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { // 检查认证对象是否是OAuth2TokenRevocationAuthenticationToken实例 if (authentication instanceof OAuth2TokenRevocationAuthenticationToken revocationToken) { // 提取token和客户端认证详情 String tokenValue = revocationToken.getToken(); // JWT Authentication clientPrincipal = (Authentication) revocationToken.getPrincipal(); // 将principal强转为Authentication String tokenTypeHint = revocationToken.getTokenTypeHint(); // Token类型 [access_token, refresh_token] String principalName = clientPrincipal.getName(); // 提取principal名称 try { if (validateAndRevokeToken(tokenValue, tokenTypeHint, principalName)) { return new OAuth2TokenRevocationAuthenticationToken(revocationToken.getToken(), clientPrincipal, tokenTypeHint); } } catch (OAuth2AuthenticationException e) { // 这就是我想要用CustomRevocationErrorResponseHandler捕获处理的异常 logger.error("{} - {}", "CustomRevocationAuthenticationProvider.authenticate", e.getError()); throw e; // throw new AuthenticationServiceException(e.getError().getDescription(), e); } catch (Exception e) { // 捕获包括SQL异常在内的所有其他异常 logger.error("{} - {}", "CustomRevocationAuthenticationProvider.authenticate", e); throw new OAuth2AuthenticationException(new OAuth2Error(SERVER_ERROR, "An error occurred while processing the token revocation request", null), e); } } return null; } // 自定义的token验证逻辑 private boolean validateAndRevokeToken(String tokenValue, String tokenType, String principalName) { try { OAuth2Authorization authorization = authorizationService.findByToken(tokenValue, OAuth2TokenType.ACCESS_TOKEN); if (authorization == null) { return false; // Token不存在 } int deletedRows; if(tokenType != null && tokenType.equalsIgnoreCase(OAuth2TokenType.ACCESS_TOKEN.getValue())) { // 撤销逻辑 //Optional<Oauth2ClientIdDTO> oauth2ClientIdDTO = tokenRevocationRepository.findClientIdByAccessTokenValue(tokenValue); //validateClientIdDto(oauth2ClientIdDTO, principalName, authorization); // 如果校验失败会抛出OAuth2AuthenticationException //deletedRows = tokenRevocationRepository.deleteByAccessTokenValue(tokenValue); } else if (tokenType != null && tokenType.equalsIgnoreCase(OAuth2TokenType.REFRESH_TOKEN.getValue())) { // 撤销逻辑 //Optional<Oauth2ClientIdDTO> oauth2ClientIdDTO = tokenRevocationRepository.findClientIdByRefreshTokenValue(tokenValue); //validateClientIdDto(oauth2ClientIdDTO, principalName, authorization); // 如果校验失败会抛出OAuth2AuthenticationException //deletedRows = tokenRevocationRepository.deleteByRefreshTokenValue(tokenValue); } return true; } catch (Exception e) { throw new RuntimeException(e); } } }
备注:内容来源于stack exchange,提问作者fforfabio




