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

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配置全局规则,让开发环境下所有方法都能被访问:

  1. 首先注入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;
}
  1. 修改@EnableMethodSecurity注解:
@EnableMethodSecurity(
        securedEnabled = true,
        defaultAuthorization = "@environment.getActiveProfiles().contains('development') or hasAuthority('ROLE_ADMIN')"
)
  1. 调整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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:30:55