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

如何在Spring WebFlux中实现自定义Spring Security PreAuthentication并完成MVC迁移?

Great question! Migrating pre-authentication from Spring MVC to WebFlux does require shifting from servlet-based filters to reactive web components, but once you get the hang of the core reactive security APIs, it's straightforward. Let's walk through this step by step.

1. Understand the Reactive Pre-Authentication Difference

First, forget about Servlet Filters—WebFlux uses ServerWebFilter and reactive-specific security components instead. The core pieces you'll need are:

  • ServerAuthenticationConverter: Extracts pre-authenticated credentials (like headers, cookies, or request attributes) from a ServerWebExchange (the reactive equivalent of HttpServletRequest).
  • AuthenticationWebFilter: The reactive filter that uses your converter to create an Authentication token, then delegates to a ServerAuthenticationManager for validation.
  • ServerAuthenticationManager: Reactive counterpart of the standard AuthenticationManager, responsible for verifying the pre-authenticated token.
2. Step-by-Step Implementation

Step 1: Build a Custom ServerAuthenticationConverter

This replaces your MVC filter's logic for extracting the principal/credentials. For example, if you were using request headers for pre-auth:

import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.security.web.server.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

public class CustomPreAuthConverter implements ServerAuthenticationConverter {

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        // Extract your pre-authenticated principal (e.g., from a header)
        return Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst("X-PREAUTH-USER-ID"))
                .map(userId -> {
                    // Create a pre-authenticated token (credentials can be null if not needed)
                    return new PreAuthenticatedAuthenticationToken(userId, null, null);
                });
    }
}

Step 2: Configure a Reactive Authentication Manager

You need a ServerAuthenticationManager to validate the token. If you're using a user details service, use ReactiveUserDetailsService for non-blocking lookups:

import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import reactor.core.publisher.Mono;

public class PreAuthAuthenticationManager implements ReactiveAuthenticationManager {

    private final ReactiveUserDetailsService userDetailsService;

    public PreAuthAuthenticationManager(ReactiveUserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        String userId = authentication.getName();
        return userDetailsService.findByUsername(userId)
                .map(userDetails -> new UsernamePasswordAuthenticationToken(
                        userDetails,
                        authentication.getCredentials(),
                        userDetails.getAuthorities()
                ))
                .switchIfEmpty(Mono.error(new RuntimeException("Invalid pre-authenticated user")));
    }
}

Step 3: Wire Up the Security Filter Chain

Register your converter and manager in a SecurityWebFilterChain, using AuthenticationWebFilter to tie everything together:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;

@Configuration
@EnableWebFluxSecurity
public class WebFluxSecurityConfig {

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,
                                                        CustomPreAuthConverter preAuthConverter,
                                                        ReactiveAuthenticationManager preAuthAuthManager) {
        // Create the pre-auth filter
        AuthenticationWebFilter preAuthFilter = new AuthenticationWebFilter(preAuthAuthManager);
        preAuthFilter.setServerAuthenticationConverter(preAuthConverter);

        // Add the filter to the chain (place it before other auth filters)
        return http
                .authorizeExchange(exchanges -> exchanges
                        .anyExchange().authenticated()
                )
                .addFilterAt(preAuthFilter, ServerHttpSecurity.SecurityWebFiltersOrder.AUTHENTICATION)
                .build();
    }

    @Bean
    public CustomPreAuthConverter customPreAuthConverter() {
        return new CustomPreAuthConverter();
    }

    @Bean
    public ReactiveAuthenticationManager preAuthAuthManager(ReactiveUserDetailsService userDetailsService) {
        return new PreAuthAuthenticationManager(userDetailsService);
    }
}

Step 4: Add Success/Failure Handling (Optional)

If you need to handle post-authentication actions (like logging or setting response headers), implement ServerAuthenticationSuccessHandler or ServerAuthenticationFailureHandler and attach them to your AuthenticationWebFilter:

preAuthFilter.setAuthenticationSuccessHandler((exchange, auth) -> {
    // Custom success logic (e.g., add a response header)
    exchange.getResponse().getHeaders().add("X-AUTH-SUCCESS", "true");
    return Mono.empty();
});
3. Migrating from Your MVC Filter-Based Setup

Here's how to map your existing MVC components to WebFlux:

  • AbstractPreAuthenticatedProcessingFilter → Replace with AuthenticationWebFilter + ServerAuthenticationConverter (your extraction logic moves to the converter).
  • PreAuthenticatedAuthenticationManager (MVC) → Replace with ReactiveAuthenticationManager (ensure all logic uses Mono/Flux for non-blocking operations).
  • PreAuthenticatedAuthenticationSuccessHandler → Replace with ServerAuthenticationSuccessHandler.
  • Filter Registration → Instead of registering a servlet filter, configure everything in SecurityWebFilterChain.
Key Tips to Avoid Issues
  • Never use Servlet Filters in WebFlux: They block threads and break reactive behavior. Stick to ServerWebFilter implementations.
  • Use Reactive Dependencies: If you're looking up user data, use ReactiveUserDetailsService instead of the traditional UserDetailsService to keep everything non-blocking.
  • Filter Order Matters: Place your pre-auth filter before other authentication filters (like OAuth2 or Basic Auth) so it takes precedence.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:21:47