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

从Spring Security迁移整合至Spring Reactive Security方案咨询

问题:Spring Security整合到Reactive Security及网关安全配置冲突问题

我有一个基于Spring Security和Spring Cloud路由的Java项目,了解到Spring Cloud路由适配Spring Reactive Security,两者的认证管理器与认证提供者存在差异。现在想咨询两个问题:

  1. 如何将现有Spring Security配置整合到Reactive Security?
  2. 能否关闭网关应用的安全配置直接使用现有配置?之前尝试这么做时,出现了Spring Reactive Security与Spring Security的Bean冲突问题。

以下是现有常规安全配置及安全过滤器链代码:

常规安全配置

@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {

    private final IUserProxy userProxy;


    @Bean
    public UserDetailsService userDetailsService() {
        return details -> {
            String[] userDetails = details.split("/");
            String serviceProvider = userDetails[0];
            String username = userDetails[1];

            return userProxy.getUserByUsernameAndServiceProviderName(username, serviceProvider);
        };
    }
    
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        
        return authenticationProvider;
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecretGenerator secretGenerator(){
        return new DefaultSecretGenerator();
    }

    @Bean
    public QrGenerator qrGenerator(){
        return new ZxingPngQrGenerator();
    }

    @Bean
    public CodeVerifier codeVerifier(){
        CodeGenerator codeGenerator = new DefaultCodeGenerator();
        TimeProvider timeProvider = new SystemTimeProvider();

        return new DefaultCodeVerifier(codeGenerator, timeProvider);
    }
}

安全过滤器链配置

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {
    
    private final JwtAuthenticationFilter authenticationFilter;
    private final AuthenticationProvider authenticationProvider;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf()
        .disable()
        .authorizeHttpRequests()
        .requestMatchers("/mng164/auth/**")
        .permitAll()
        .anyRequest()
        .authenticated()
        .and()
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authenticationProvider(authenticationProvider)
        .addFilterBefore(authenticationFilter, UsernamePasswordAuthenticationFilter.class);
        
        return http.build();
    }

}

解决方案

一、将现有Spring Security配置迁移到Reactive Security

Reactive Security基于WebFlux,和传统Servlet栈的Spring Security在API设计上有差异,需要针对性调整核心组件:

1. 替换UserDetailsService为ReactiveUserDetailsService

传统UserDetailsService是阻塞式的,Reactive环境要改用ReactiveUserDetailsService,如果你的userProxy是阻塞调用,用Mono.fromCallable包装成响应式类型:

@Bean
public ReactiveUserDetailsService reactiveUserDetailsService() {
    return username -> {
        String[] userDetails = username.split("/");
        String serviceProvider = userDetails[0];
        String actualUsername = userDetails[1];
        
        // 包装阻塞调用为Reactive类型,指定线程池避免阻塞事件循环
        return Mono.fromCallable(() -> 
            userProxy.getUserByUsernameAndServiceProviderName(actualUsername, serviceProvider)
        ).subscribeOn(Schedulers.boundedElastic());
    };
}

2. 替换AuthenticationProvider和AuthenticationManager

Reactive环境用ReactiveAuthenticationManager替代传统AuthenticationManager,DaoAuthenticationProvider对应ReactiveDaoAuthenticationProvider:

@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager(ReactiveUserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
    ReactiveDaoAuthenticationProvider authProvider = new ReactiveDaoAuthenticationProvider();
    authProvider.setUserDetailsService(userDetailsService);
    authProvider.setPasswordEncoder(passwordEncoder);
    return authProvider;
}

3. 重构SecurityFilterChain为Reactive版本

使用ServerHttpSecurity替代HttpSecurity,配置响应式安全过滤器链:

@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
public class ReactiveSecurityConfiguration {
    
    private final JwtAuthenticationWebFilter jwtAuthenticationWebFilter; // 替换为响应式JWT过滤器
    private final ReactiveAuthenticationManager reactiveAuthenticationManager;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http.csrf(ServerHttpSecurity.CsrfSpec::disable)
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/mng164/auth/**").permitAll()
                        .anyExchange().authenticated()
                )
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authenticationManager(reactiveAuthenticationManager)
                .addFilterBefore(jwtAuthenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
                .build();
    }
}

4. 调整JWT过滤器为响应式

传统JwtAuthenticationFilter基于Servlet的OncePerRequestFilter,要改成实现WebFilter的响应式过滤器:

@Component
public class JwtAuthenticationWebFilter implements WebFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            // 这里实现JWT解析、验证逻辑,生成Authentication对象
            Authentication auth = ...;
            // 将认证信息存入Reactive安全上下文
            return chain.filter(exchange)
                    .contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth));
        }
        return chain.filter(exchange);
    }
}

二、解决网关安全配置冲突问题

不能直接关闭网关的Reactive Security配置并复用传统Spring Security配置——网关是基于WebFlux的响应式环境,传统Servlet栈的Spring Security组件(如HttpSecurity、UsernamePasswordAuthenticationFilter)无法在WebFlux环境中正常工作,这就是Bean冲突的根源。

正确处理方式:

  1. 移除网关中传统Spring Security依赖:确保网关只引入响应式安全依赖,排除Servlet栈的Spring Security模块:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  1. 统一用Reactive Security配置:网关完全采用响应式安全配置,不要混合两种环境的组件。如果需要复用业务系统的认证逻辑,可将核心逻辑(如用户查询、密码验证)封装成独立服务或组件,在响应式配置中调用。

  2. 避免重复Bean定义:把通用Bean(如PasswordEncoder、SecretGenerator)提取到公共模块,避免网关和业务系统重复创建相同Bean引发冲突。

内容的提问来源于stack exchange,提问作者Aviel Avitan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:25:34