在OAuth2中处理AuthenticationException:ResourceServerConfigurerAdapter场景方案咨询
我之前在Spring Security OAuth2资源服务器项目里也踩过这个坑——用@ControllerAdvice和@ExceptionHandler根本抓不到认证阶段抛出的AuthenticationException,后来才搞明白:这些异常是在Spring Security的过滤器链里抛出的,还没到Controller层呢,所以Controller层面的异常处理器根本碰不到它们。
你之前试的AuthenticationFailureHandler确实不适用,因为它主要是处理表单登录、用户名密码认证失败这类场景,而资源服务器的认证逻辑是基于Token校验的,走的是OAuth2专属的过滤器流程,所以那套方案行不通。
下面给你两个亲测有效的解决方案:
方案一:自定义AuthenticationEntryPoint处理未认证异常
AuthenticationEntryPoint是Spring Security专门用来处理未认证请求的接口,包括各种AuthenticationException(比如Token无效、Token过期、没有携带Token等)。你可以实现这个接口,自定义返回的响应格式,然后配置到ResourceServer的安全配置里。
1. 实现自定义AuthenticationEntryPoint
@Component public class CustomAuthEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { // 设置响应格式和状态码 response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 自定义返回内容 Map<String, Object> result = new HashMap<>(); result.put("code", 401); result.put("message", "认证失败:" + authException.getMessage()); result.put("timestamp", LocalDateTime.now().toString()); // 写入响应 ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writeValue(response.getOutputStream(), result); } }
2. 在ResourceServerConfig中配置
@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Autowired private CustomAuthEntryPoint customAuthEntryPoint; @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() // 所有请求都需要认证 .and() .exceptionHandling() .authenticationEntryPoint(customAuthEntryPoint); // 配置自定义的认证异常处理器 } }
方案二:额外配置AccessDeniedHandler处理权限不足异常
如果你的项目还需要处理AccessDeniedException(用户已认证但权限不足),可以再自定义一个AccessDeniedHandler,和上面的EntryPoint一起配置:
1. 实现自定义AccessDeniedHandler
@Component public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException { response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); Map<String, Object> result = new HashMap<>(); result.put("code", 403); result.put("message", "权限不足:" + accessDeniedException.getMessage()); result.put("timestamp", LocalDateTime.now().toString()); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.writeValue(response.getOutputStream(), result); } }
2. 更新ResourceServerConfig配置
@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Autowired private CustomAuthEntryPoint customAuthEntryPoint; @Autowired private CustomAccessDeniedHandler customAccessDeniedHandler; @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .exceptionHandling() .authenticationEntryPoint(customAuthEntryPoint) .accessDeniedHandler(customAccessDeniedHandler); // 新增权限不足处理器 } }
为什么这个方案可行?
因为AuthenticationEntryPoint和AccessDeniedHandler是Spring Security过滤器链层面的处理器,它们能直接捕获过滤器链中抛出的认证、权限相关异常,这正是@ControllerAdvice触及不到的区域,完美适配ResourceServer的场景。
内容的提问来源于stack exchange,提问作者Vamsi Vegesna

