为何Spring Security在此场景下会重定向至/error?
问题原因分析
你看到的Securing GET /error是因为**response.sendError(HttpServletResponse.SC_FORBIDDEN)触发了Servlet容器的错误页面转发机制**。
当调用sendError()时,Servlet容器会判定需要展示错误页面,自动将请求转发到Spring Boot默认配置的/error端点(由ErrorMvcAutoConfiguration自动注册)。而你的SecurityFilterChain配置了anyRequest().authenticated(),所以/error请求也会经过Spring Security过滤器链,导致日志中出现第二次过滤器调用记录。
另外,虽然你在sendError()之后写了JSON响应,但容器的错误转发会覆盖你输出的内容,最终客户端收到的是Spring Boot默认的错误响应,而非你自定义的JSON。
解决方案
修改ApplicationAuthenticationEntryPoint的commence方法,不要使用response.sendError(),直接设置响应状态码、内容类型并写入JSON内容:
public class ApplicationAuthenticationEntryPoint implements AuthenticationEntryPoint { private final ObjectMapper mapper = new ObjectMapper(); @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { // 直接设置状态码,不触发容器错误转发 response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding("UTF-8"); ObjectNode errorNode = mapper.createObjectNode() .put("timestamp", LocalDateTime.now().toEpochSecond(ZoneOffset.of("-3"))) .put("message", "Access denied to resource"); response.getWriter().write(mapper.writeValueAsString(errorNode)); } }
额外说明
- 移除
sendError()后,Servlet容器不会再转发到/error,过滤器链只会执行一次,你的自定义JSON响应会直接返回给客户端。 - 如果需要统一处理其他错误场景(比如控制器抛出的异常),可以单独配置
@ControllerAdvice来处理,避免和Spring Security的认证异常处理冲突。
内容的提问来源于stack exchange,提问作者Matheus
相关产品推荐
相关产品推荐

