Spring Security忽略自定义AuthenticationEntryPoint问题排查求助
这个问题其实是两个核心点导致的:混淆了Spring Security中认证异常与授权异常的处理组件,再加上Spring Boot 2.1.x版本默认错误处理机制的介入,最终导致403异常被重定向到不存在的/error端点,引发404错误。
核心错误:用错了异常处理组件
你当前配置的authenticationEntryPoint是专门用来处理未认证请求的(比如用户完全没登录就访问受保护资源),而你的场景是用户已认证但权限不足(匹配hasAnyRole规则失败)——这种授权失败的情况,应该用accessDeniedHandler来处理,而非authenticationEntryPoint。
在Spring Security 5.1.x + Spring Boot 2.1.x的组合中,如果未配置accessDeniedHandler,授权失败时会默认转发到Spring Boot的/error端点;如果你没有自定义这个端点,自然就会返回404错误。
修正步骤
1. 实现自定义的AccessDeniedHandler
创建一个处理403授权失败的类,直接返回403响应,避免触发转发逻辑:
public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { // 直接设置403状态码并返回自定义响应(这里用JSON举例,你可以根据需求调整) response.setStatus(HttpServletResponse.SC_FORBIDDEN); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\": 403, \"message\": \"Insufficient permissions to access this resource\"}"); // 如果不需要JSON响应,也可以直接调用sendError // response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied"); } }
2. 修正Spring Security配置
在你的配置中添加accessDeniedHandler,同时保留authenticationEntryPoint处理未认证场景:
http.exceptionHandling() .authenticationEntryPoint(new Http403ForbiddenEntryPoint()) // 处理未认证请求 .accessDeniedHandler(new CustomAccessDeniedHandler()) // 处理授权失败(403)场景 .and() .addFilter(headerAuthenticationFilter) .httpBasic().disable() .csrf().disable() .authenticationProvider(preAuthenticatedAuthenticationProvider) .authorizeRequests() .antMatchers("/api/**") .hasAnyRole("ROLE1", "ROLE2") .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
3. 可选:调整Spring Boot错误配置(避免意外重定向)
为了彻底防止Spring Boot默认错误机制介入,可以在application.properties或application.yml中添加以下配置:
# 关闭白标错误页,避免默认的错误页面干扰 server.error.whitelabel.enabled=false
额外检查点
确认你的Http403ForbiddenEntryPoint实现是正确的——它应该直接返回403响应,而非触发重定向,比如:
public class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Authentication required"); } }
如果这个类里包含重定向逻辑,也会导致类似的异常跳转问题。
内容的提问来源于stack exchange,提问作者Tranquilized

