You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring Boot 3后@PreAuthorize注解失效问题求助

问题说明

我已将Spring Boot项目升级至Spring Boot 3,并更新了WebSecurityConfig配置类,但带有@PreAuthorize注解的接口权限控制失效——仅拥有USER角色的已登录用户能访问所有接口,导致两个测试用例执行失败。

当前CustomWebSecurityConfig配置

// imports...

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class CustomWebSecurityConfig {
    final UserDetailsServiceImpl userDetailsService;

    private final AuthEntryPointJwt unauthorizedHandler;
    private final PasswordEncoder passwordEncoder;

    @Bean
    public AuthTokenFilter authenticationJwtTokenFilter() {
        return new AuthTokenFilter();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();

        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder);

        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
        return authConfig.getAuthenticationManager();
    }

    /**
     * Sets up a chain of antmatchers specifying what permissions and roles have access to which resources.
     *
     * @param http          Injected HttpSecurity object
     * @return              Chain of Security filters
     * @throws Exception    Currently throws general exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .authorizeHttpRequests(requests -> requests.requestMatchers("/api/auth/**").permitAll()
                        .requestMatchers("/api/test/**").permitAll()
                        .requestMatchers("/").permitAll()
                        .requestMatchers("/index.html").permitAll()
                        .requestMatchers("/favicon.ico").permitAll()
                        .requestMatchers("/main.js").permitAll()
                        .requestMatchers("/polyfills.js").permitAll()
                        .requestMatchers("/runtime.js").permitAll()
                        .requestMatchers("/styles.css").permitAll()
                        .requestMatchers("/vendor.css").permitAll()
                        .requestMatchers("/assets/**").permitAll()
                        .requestMatchers("/error").permitAll()
                        .requestMatchers("/**").permitAll()
                        .anyRequest().authenticated());

        http.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authenticationProvider(authenticationProvider());

        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }
}

带@PreAuthorize注解的TestController

// imports...

@RestController
@RequestMapping("/api/test")
public class TestController {
    @GetMapping("/all")
    public String allAccess() {
        return "Public Content.";
    }

    @GetMapping("/user")
    @PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
    public String userAccess() {
        return "User Content.";
    }

    @GetMapping("/mod")
    @PreAuthorize("hasRole('MODERATOR')")
    public String moderatorAccess() {
        return "Moderator Board.";
    }

    @GetMapping("/admin")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminAccess() {
        return "Admin Board.";
    }
}

执行失败的测试用例

@Test
@DisplayName("普通用户无法访问MODERATOR接口")
@WithMockUser(roles = "USER")
void givenUserToken_whenGetSecureRequest_thenForbidden() throws Exception {
    mockMvc.perform(get("/api/test/mod"))
            .andExpect(status().isForbidden());
}

@Test
@DisplayName("普通用户无法访问ADMIN接口")
@WithMockUser(roles = "USER")
void givenUserToken_whenGetSecureRequest_thenForbidden2() throws Exception {
    mockMvc.perform(get("/api/test/admin"))
            .andExpect(status().isForbidden());
}

问题原因及修复方案

核心问题

  1. SecurityFilterChain配置错误:你在securityFilterChain中设置了.requestMatchers("/api/test/**").permitAll(),这意味着所有/api/test/**路径的请求都被直接放行,完全绕过了方法级别的@PreAuthorize校验。
  2. 缺少方法安全注解启用:Spring Boot 3中,要启用@PreAuthorize这类方法级权限控制,必须添加@EnableMethodSecurity注解(替代旧版的@EnableGlobalMethodSecurity)。

修复步骤

步骤1:修改SecurityFilterChain配置

移除.requestMatchers("/api/test/**").permitAll(),让请求进入方法级校验流程:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.cors().and().csrf().disable()
            .authorizeHttpRequests(requests -> requests.requestMatchers("/api/auth/**").permitAll()
                    // 移除/api/test/**的permitAll配置
                    .requestMatchers("/").permitAll()
                    .requestMatchers("/index.html").permitAll()
                    .requestMatchers("/favicon.ico").permitAll()
                    .requestMatchers("/main.js").permitAll()
                    .requestMatchers("/polyfills.js").permitAll()
                    .requestMatchers("/runtime.js").permitAll()
                    .requestMatchers("/styles.css").permitAll()
                    .requestMatchers("/vendor.css").permitAll()
                    .requestMatchers("/assets/**").permitAll()
                    .requestMatchers("/error").permitAll()
                    // 建议精准控制静态资源放行,避免/** permitAll覆盖所有接口
                    .anyRequest().authenticated());

    http.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

    http.authenticationProvider(authenticationProvider());

    http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);

    return http.build();
}

步骤2:启用方法级安全控制

在CustomWebSecurityConfig类上添加@EnableMethodSecurity注解,开启@PreAuthorize等注解的支持:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity // 添加这行
@RequiredArgsConstructor
public class CustomWebSecurityConfig {
    // ... 原有代码不变
}

步骤3:验证测试用例

修改完成后,重新运行测试用例,此时USER角色的用户访问/api/test/mod和/api/test/admin会返回403 Forbidden,测试用例即可通过。

内容的提问来源于stack exchange,提问作者Davide Marcoli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:30:26