Spring Security中配置AccessDeniedHandler没有生效

本文探讨了如何在Spring Security中正确配置访问控制,确保@PreAuthorize抛出的AccessDeniedException被自定义的accessDeniedHandler捕获。同时介绍了全局异常处理器的角色,并提供了实例代码以解决权限不足时的错误处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现象

在 WebSecurityConfigurerAdapter 配置了如下代码:

// 自定义未授权和未登录异常
http.exceptionHandling()
        .accessDeniedHandler(new RestAccessDeniedHandler())
        .authenticationEntryPoint(new RestAuthenticationEntryPoint());

在 Controller 层 REST 接口中添加有 @PreAuthorize 注解:

@PreAuthorize(value = "hasAuthority('Users.Update')")
@GetMapping("/hello")
public ResponseEntity<?> hello(@RequestParam(value = "name", required = false, defaultValue = "Tom") String name) {
    return ResponseEntity.ok(RestResponse.buildResponse("Hi: " + name));
}

访问接口 /hello,报服务端 500 错误,没有执行我们设置的 accessDeniedHandler 来处理权限不足的异常。

原因

@PreAuthorize 注解的异常,抛出 AccessDeniedException 异常,不会被 accessDeniedHandler 捕获,而是会被全局异常捕获。全局异常处理 AccessDeniedException 的相关示例代码:

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<RestResponse<Object>> handleException(Exception exception) {
        String message = exception.getLocalizedMessage();
        log.error("全局异常捕获Exception:{}", message, exception);
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        if (exception instanceof BadCredentialsException) {
            httpStatus = HttpStatus.UNAUTHORIZED;
        }
        if (exception instanceof HttpRequestMethodNotSupportedException) {
            httpStatus = HttpStatus.METHOD_NOT_ALLOWED;
        }
        return RestResponse.buildError(httpStatus, message);
    }

    @ExceptionHandler(CommonException.class)
    public ResponseEntity<RestResponse<Object>> handleException(CommonException exception) {
        String message = exception.getLocalizedMessage();
        log.error("全局异常捕获CommonException:{}", message);
        return RestResponse.buildError(exception.getBusinessStatus(), message);
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<RestResponse<Object>> handleException(AccessDeniedException exception) {
        String message = exception.getLocalizedMessage();
        log.error("全局异常捕获AccessDeniedException:{}", message);
        return RestResponse.buildError(HttpStatus.FORBIDDEN, Forbidden);
    }

}

如果需要被 accessDeniedHandler 捕获处理,则需要这么写 WebSecurityConfigurerAdapter 的代码:

http.cors().and()
        .authorizeRequests().antMatchers("/hello0").permitAll()
        // 注意hasRole、hasAuthority 如果出现异常,会调用设置的 accessDeniedHandler 方法
        .antMatchers("/hello").hasAuthority("Users.Update")
        .anyRequest().authenticated();
        
// 自定义未授权和未登录异常
http.exceptionHandling()
        .accessDeniedHandler(new RestAccessDeniedHandler())
        .authenticationEntryPoint(new RestAuthenticationEntryPoint());

<think>好的,我现在需要帮助用户解决Spring Security配置白名单后仍然出现403的问题。首先,我要回忆一下Spring Security的基本配置流程,特别是关于白名单的设置。根据用户提供的引用内容,特别是引用[2]和引用[4],可能的问题点有几个方面。 首先,用户提到白名单路径配置是否正确。根据引用[2],配置类需要明确指定哪些路径不需要拦截,这可能涉及到使用`antMatchers`或`requestMatchers`方法,并且需要结合`permitAll()`方法。如果用户没有正确使用这些方法,或者路径模式写错了,就会导致白名单失效。例如,用户可能配置了`/public/**`,但实际访问的是`/public`,缺少了通配符,导致不匹配。 其次,引用[4]指出,如果禁用了默认的登录页面(比如注释掉`http.formLogin()`),但没有提供自定义的认证入口点,当未认证的用户访问受保护的资源时,系统可能直接返回403而不是跳转到登录页。这时候,即使用户访问的是白名单路径,如果配置有误,也可能被误判为需要认证的资源。 另外,引用[3]提到Spring Security 5.7及以上版本弃用了`WebSecurityConfigurerAdapter`,如果用户还在使用旧的配置方式,可能会遇到兼容性问题。需要检查用户的配置类是否采用了新的方式,比如通过`SecurityFilterChain` Bean来配置。 还有一个可能的问题是请求方法不匹配。比如,用户配置了`antMatchers(HttpMethod.GET, "/api/public")`,但实际发送的是POST请求,这时候也会导致403。需要确认白名单是否覆盖了所有需要的HTTP方法。 此外,权限配置顺序也很重要。Spring Security配置是按照顺序生效的,如果先配置了更具体的路径,后面的白名单可能被覆盖。例如,如果先配置了`anyRequest().authenticated()`,再配置白名单,那么白名单就不会生效。需要确保白名单的配置在更通用的规则之前。 最后,可能需要检查是否存在其他安全过滤器干扰。比如自定义的JWT过滤器(如`JwtAuthenticationTokenFilter`)可能在没有令牌的情况下直接拒绝请求,导致403。需要确认过滤器是否正确配置,对于白名单路径是否放行。 综合以上可能的原因,我需要一步步引导用户检查他们的配置,确认路径是否正确,权限配置顺序是否正确,是否处理了未认证的情况,以及是否使用了新版本的配置方式。同时,建议用户检查日志,查看具体的拒绝原因,这有助于快速定位问题。</think>### Spring Security 白名单配置后仍出现403的解决方案 #### 1. 确认白名单配置语法正确性 - **新版配置方式(推荐)**: Spring Security 5.7+ 已弃用 `WebSecurityConfigurerAdapter`,需通过 `SecurityFilterChain` Bean 配置: ```java @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/public/**", "/login").permitAll() // 白名单路径 .anyRequest().authenticated() ) .formLogin().disable(); // 禁用默认登录页[^4] return http.build(); } ``` - **关键点**: - 使用 `requestMatchers` 或 `antMatchers` 明确白名单路径。 - 路径需包含**所有需要放行的HTTP方法**(如 `GET`, `POST`)。 - 确保白名单配置在 `.anyRequest().authenticated()` **之前**,否则优先级覆盖会导致失效[^2]。 #### 2. 检查权限校验流程完整性 若禁用默认登录页(如 `http.formLogin().disable()`),需配置**自定义认证入口点**,避免未认证请求直接返回403: ```java @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(new RestAuthenticationEntryPoint()) // 处理未认证请求 .accessDeniedHandler(new RestfulAccessDeniedHandler()) // 处理权限不足 ) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/public/**").permitAll() .anyRequest().authenticated() ); return http.build(); } ``` - **作用**: - `RestAuthenticationEntryPoint`:引导未认证用户跳转登录或返回401。 - `RestfulAccessDeniedHandler`:自定义权限不足时的响应(如返回JSON提示)。 #### 3. 排查过滤器干扰 若项目中存在**自定义过滤器**(如JWT过滤器),需确保其对白名单路径放行: ```java public class JwtFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) { if (isWhiteListPath(request)) { // 判断是否为白名单路径 chain.doFilter(request, response); // 直接放行 return; } // 其他逻辑(如校验Token) } } ``` - **关键点**: 过滤器需在Spring Security过滤器链之前执行,或通过配置跳过白名单路径。 #### 4. 验证请求匹配规则 - **路径匹配规则**: - 使用 `/**` 匹配多级路径(如 `/public/**` 包含 `/public/a/b`)。 - 使用 `/*` 仅匹配单级路径(如 `/public/*` 不包含 `/public/a/b`)。 - **HTTP方法匹配**: 若白名单需支持所有方法,需明确指定: ```java .requestMatchers(HttpMethod.GET, "/api/read").permitAll() .requestMatchers(HttpMethod.POST, "/api/write").permitAll() ``` #### 5. 调试与日志分析 - **启用调试日志**: 在 `application.properties` 中添加: ```properties logging.level.org.springframework.security=DEBUG ``` - **日志关键信息**: - 请求是否命中白名单路径。 - 过滤器链是否中断请求。 - 权限校验失败的具体原因(如 `AccessDeniedException`)。 #### 6. 其他常见问题 - **静态资源未放行**: 若白名单包含静态资源(如 `/css/**`),需确认资源路径与实际请求一致。 - **CSRF保护干扰**: 对API接口可禁用CSRF(非Web页面场景): ```java http.csrf().disable(); ``` --- ### 示例配置Spring Security 6.x) ```java @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/", "/login", "/public/**").permitAll() .anyRequest().authenticated() ) .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(new RestAuthenticationEntryPoint()) .accessDeniedHandler(new RestfulAccessDeniedHandler()) ) .csrf().disable(); return http.build(); } } ``` ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心如光

您的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值