在使用 SpringSecurityOAuth2 中,用户名或密码失败可以使用 InvalidGrantException 来捕获,账户禁用等异常可以使用 InternalAuthenticationServiceException 来捕获,但是客户端认证失败(无效的凭据)无法使用全局异常处理器来捕获,因为是发生在 filter ,此时还没有到 DispatcherServlet 请求处理流程上,我们可以查看源码进行分析,找到对应的解决方案
认证流程
客户端的认证处理顺序是 ClientCredentialsTokenEndpointFilter - OAuth2AuthenticationEntryPoint - AbstractOAuth2SecurityExceptionHandler#doHandle,可以看到 result 就是返回的结果。
解决方案
既然已经知道认证失败异常是通过 OAuth2AuthenticationEntryPoint 来得到响应结果的,所以我们可以重写 ClientCredentialsTokenEndpointFilter 并使用自定义的 OAuth2AuthenticationEntryPoint 进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| @Slf4j @RequiredArgsConstructor @Configuration @EnableAuthorizationServer public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {
@Bean public AuthenticationEntryPoint authenticationEntryPoint() { return (request, response, e) -> { log.error("无效凭据:", e); response.setStatus(HttpStatus.OK.value()); response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); response.setHeader(HttpHeaders.CACHE_CONTROL, "no-cache"); response.setCharacterEncoding(StandardCharsets.UTF_8.name()); ServerResponse serverResponse = ServerResponse.createByError(ResultEnum.INVALID_CLIENT); response.getWriter().print(JSONUtil.toJson(serverResponse)); response.getWriter().flush(); }; }
@Override public void configure(AuthorizationServerSecurityConfigurer security) { CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security); endpointFilter.afterPropertiesSet(); endpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint());
security.addTokenEndpointAuthenticationFilter(endpointFilter); security.authenticationEntryPoint(authenticationEntryPoint()) .tokenKeyAccess("isAuthenticated()") .checkTokenAccess("permitAll()"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public class CustomClientCredentialsTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {
private AuthorizationServerSecurityConfigurer configurer;
private AuthenticationEntryPoint authenticationEntryPoint;
public CustomClientCredentialsTokenEndpointFilter(AuthorizationServerSecurityConfigurer configurer) { this.configurer = configurer; }
@Override public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) { super.setAuthenticationEntryPoint(null); this.authenticationEntryPoint = authenticationEntryPoint; }
@Override protected AuthenticationManager getAuthenticationManager() { return configurer.and().getSharedObject(AuthenticationManager.class); }
@Override public void afterPropertiesSet() { setAuthenticationFailureHandler((request, response, exception) -> authenticationEntryPoint.commence(request, response, exception)); setAuthenticationSuccessHandler((request, response, authentication) -> { }); } }
|
测试
原来的返回结果为
1 2 3 4
| { "error": "invalid_client", "error_description": "Bad client credentials" }
|
现在的返回结果
1 2 3 4
| { "status": 1060, "msg": "无效凭据" }
|