Spring Security 6开发环境禁用安全校验及@Secured权限问题解决
问题:Spring Security 6开发环境下禁用所有安全校验(含@Secured方法)
我想在Spring Security 6的development环境下完全禁用安全校验,不需要JWT令牌或特殊角色就能访问所有接口,包括带有@Secured注解的方法。
当前的安全配置代码如下:
@Configuration @EnableWebSecurity @EnableMethodSecurity( securedEnabled = true ) public class WebSecurityConfig { private final UserDetailsServiceImpl userDetailsService; private final AuthJwtEntrypoint unauthorizedHandler; private final FilterChainExceptionHandler filterChainExceptionHandler; public WebSecurityConfig(UserDetailsServiceImpl userDetailsService, AuthJwtEntrypoint unauthorizedHandler, FilterChainExceptionHandler filterChainExceptionHandler) { this.userDetailsService = userDetailsService; this.unauthorizedHandler = unauthorizedHandler; this.filterChainExceptionHandler = filterChainExceptionHandler; } @Bean public JwtAuthenticationFilter jwtAuthenticationFilter() { return new JwtAuthenticationFilter(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManagerBean(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService); authProvider.setPasswordEncoder(passwordEncoder()); return authProvider; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); httpSecurity.authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/country/**").permitAll() .anyRequest().authenticated() ); httpSecurity.authenticationProvider(authenticationProvider()); httpSecurity.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); httpSecurity.addFilterBefore(filterChainExceptionHandler, LogoutFilter.class); return httpSecurity.build(); } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080", "http://127.0.0.1:8080")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("Authorization", "content-type")); configuration.setExposedHeaders(Arrays.asList("Authorization")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
我尝试通过判断激活环境修改SecurityFilterChain,在development环境下允许所有请求:
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); if (Arrays.asList(environment.getActiveProfiles()).contains("development")) { // In the development profile, permit all requests. httpSecurity.authorizeHttpRequests(auth -> auth.anyRequest().permitAll() ); } else { // In other profiles, use your regular security configuration. httpSecurity.authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/country/**").permitAll() // ... (other authorization rules) .anyRequest().authenticated() ); } httpSecurity.authenticationProvider(authenticationProvider()); httpSecurity.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); httpSecurity.addFilterBefore(filterChainExceptionHandler, LogoutFilter.class); return httpSecurity.build(); }
现在无JWT的请求能正常访问接口,但带有@Secured注解的方法还是返回权限拒绝,该怎么解决?
解决方案
问题核心是方法级安全(@Secured)不受SecurityFilterChain的permitAll控制,方法级安全是独立的校验逻辑,就算请求被过滤器放行,方法上的注解依然会触发权限检查。以下是几种可行的解决方式:
方法一:拆分环境配置类(最彻底)
通过@Profile注解分别为开发环境和生产环境创建独立的安全配置,开发环境下完全禁用方法级安全:
生产/非开发环境配置类
@Configuration @EnableWebSecurity @Profile("!development") @EnableMethodSecurity(securedEnabled = true) public class WebSecurityConfig { // 保留原有的所有配置代码(认证提供者、JWT过滤器、权限规则等) }
开发环境配置类
@Configuration @EnableWebSecurity @Profile("development") public class DevWebSecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) // 禁用认证入口,避免未认证时抛出异常 .exceptionHandling(exception -> exception.authenticationEntryPoint((request, response, authException) -> { response.setStatus(HttpServletResponse.SC_OK); })) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // 允许所有请求 .authorizeHttpRequests(auth -> auth.anyRequest().permitAll()); // 移除不必要的认证组件和过滤器,简化链路 return httpSecurity.build(); } @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:8080", "http://127.0.0.1:8080")); configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); configuration.setAllowedHeaders(Arrays.asList("Authorization", "content-type")); configuration.setExposedHeaders(Arrays.asList("Authorization")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
这种方式完全隔离了不同环境的安全逻辑,开发环境下不会触发任何方法级校验。
方法二:通过SpEL全局放行开发环境的方法校验
在原配置类中,通过@EnableMethodSecurity的defaultAuthorization配置全局规则,让开发环境下所有方法都能被访问:
- 首先注入
Environment:
private final Environment environment; public WebSecurityConfig(UserDetailsServiceImpl userDetailsService, AuthJwtEntrypoint unauthorizedHandler, FilterChainExceptionHandler filterChainExceptionHandler, Environment environment) { this.userDetailsService = userDetailsService; this.unauthorizedHandler = unauthorizedHandler; this.filterChainExceptionHandler = filterChainExceptionHandler; this.environment = environment; }
- 修改
@EnableMethodSecurity注解:
@EnableMethodSecurity( securedEnabled = true, defaultAuthorization = "@environment.getActiveProfiles().contains('development') or hasAuthority('ROLE_ADMIN')" )
- 调整
SecurityFilterChain,为开发环境添加匿名认证(避免方法校验时因未认证报错):
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) .cors(Customizer.withDefaults()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); if (Arrays.asList(environment.getActiveProfiles()).contains("development")) { httpSecurity.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) // 添加匿名认证,确保方法校验时存在认证信息 .anonymous(Customizer.withDefaults()); } else { httpSecurity.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/country/**").permitAll() .anyRequest().authenticated() ) .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } httpSecurity.addFilterBefore(filterChainExceptionHandler, LogoutFilter.class); return httpSecurity.build(); }
方法三:禁用方法安全拦截器(开发环境)
通过BeanPostProcessor在开发环境下修改方法安全拦截器,让它跳过所有校验:
@Component @Profile("development") public class DevMethodSecurityDisabler implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MethodSecurityInterceptor) { // 设置空的安全元数据源,跳过所有方法级校验 ((MethodSecurityInterceptor) bean).setSecurityMetadataSource(methodInvocation -> null); } return bean; } }
这个组件会在开发环境中自动生效,无需修改原有配置类。
内容的提问来源于stack exchange,提问作者Michael_313
相关产品推荐
相关产品推荐

