升级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()); }
问题原因及修复方案
核心问题
- SecurityFilterChain配置错误:你在
securityFilterChain中设置了.requestMatchers("/api/test/**").permitAll(),这意味着所有/api/test/**路径的请求都被直接放行,完全绕过了方法级别的@PreAuthorize校验。 - 缺少方法安全注解启用: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
相关产品推荐
相关产品推荐

