从Spring Security迁移整合至Spring Reactive Security方案咨询
我有一个基于Spring Security和Spring Cloud路由的Java项目,了解到Spring Cloud路由适配Spring Reactive Security,两者的认证管理器与认证提供者存在差异。现在想咨询两个问题:
- 如何将现有Spring Security配置整合到Reactive Security?
- 能否关闭网关应用的安全配置直接使用现有配置?之前尝试这么做时,出现了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冲突的根源。
正确处理方式:
- 移除网关中传统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>
统一用Reactive Security配置:网关完全采用响应式安全配置,不要混合两种环境的组件。如果需要复用业务系统的认证逻辑,可将核心逻辑(如用户查询、密码验证)封装成独立服务或组件,在响应式配置中调用。
避免重复Bean定义:把通用Bean(如
PasswordEncoder、SecretGenerator)提取到公共模块,避免网关和业务系统重复创建相同Bean引发冲突。
内容的提问来源于stack exchange,提问作者Aviel Avitan

