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

Spring WebFlux中ReactiveSecurityContextHolder获取Authentication为null求助

问题描述

在Spring Boot WebFlux应用中,使用Spring Security实现了WebFilter,将Authentication存入ReactiveSecurityContextHolder,但获取时始终返回null。

复现代码

Spring Boot启动类

@EnableWebFlux
@SpringBootApplication
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .csrf(csrfSpec -> csrfSpec.disable())
                .authorizeExchange(auth -> auth.pathMatchers(HttpMethod.GET, "/**")
                        .authenticated())
                .httpBasic(httpBasicSpec -> httpBasicSpec.disable())
                .addFilterBefore(customWebFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
                .build();
    }
    public WebFilter customWebFilter() {
        return (exchange, chain) -> {
            Authentication authenticatedToken =
                    new PreAuthenticatedAuthenticationToken("User_PreAuthenticated", "");
            authenticatedToken.setAuthenticated(true);
            return chain.filter(exchange)
                    .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authenticatedToken));
        };
    }
}

获取Authentication的代码(返回null)

// 此处Authentication始终为null
Authentication authentication = ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.toFuture().getNow(null); 

更新补充场景

应用中使用graphql-java,需要在GraphQL DataFetcher处理查询时获取当前用户信息。Spring MVC环境下可通过ThreadLocal的SecurityContext获取,但WebFlux响应式环境中无法直接复用该方式。

GraphQL查询配置及DataFetcher代码

public List<TypeRuntimeWiring.Builder> buildWiring() {
        return Arrays.asList(newTypeWiring("Query").dataFetcher("getUserName",
                getCurrentUser()));
    }

    private DataFetcher getCurrentUser() {
        return dataFetchingEnvironment -> {
            String currentUser = "NOT_AVAILABLE";
            Authentication authentication = ReactiveSecurityContextHolder.getContext()
                    .map(SecurityContext::getAuthentication)
                    .toFuture().getNow(null);
            // 此处Authentication始终为null
            if (authentication != null) {
                Object principal = authentication.getPrincipal();
                if (principal != null && principal instanceof User) {
                    currentUser = ((User) principal).getUsername();
                }
            }
            return currentUser;
        };
    }

解决方案

核心原因

  1. 上下文传递时机错误:WebFilter中contextWrite是在chain.filter(exchange)执行后写入上下文,导致后续请求处理环节无法拿到已写入的认证信息。
  2. 脱离响应式上下文链:使用toFuture().getNow(null)这种同步阻塞方式获取认证信息,会脱离Reactor的响应式上下文,无法读取到之前存入的Authentication。
  3. GraphQL DataFetcher类型不匹配:普通DataFetcher的执行线程不在WebFlux的响应式上下文链中,无法继承ReactiveSecurityContextHolder的上下文。

具体修复步骤

1. 修正WebFilter的上下文写入逻辑

确保上下文写入在请求处理链执行前完成,保证后续环节能继承上下文:

public WebFilter customWebFilter() {
    return (exchange, chain) -> {
        Authentication authenticatedToken =
                new PreAuthenticatedAuthenticationToken("User_PreAuthenticated", "");
        authenticatedToken.setAuthenticated(true);
        // 先写入上下文,再执行后续请求链
        return Mono.defer(() -> chain.filter(exchange))
                .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authenticatedToken));
    };
}

2. 改用ReactiveDataFetcher适配响应式上下文

将普通DataFetcher替换为ReactiveDataFetcher,让其接入WebFlux的响应式上下文链:

private ReactiveDataFetcher<String> getCurrentUser() {
    return dataFetchingEnvironment -> 
        ReactiveSecurityContextHolder.getContext()
            .map(SecurityContext::getAuthentication)
            .map(auth -> {
                if (auth != null && auth.getPrincipal() instanceof User) {
                    return ((User) auth.getPrincipal()).getUsername();
                }
                return "NOT_AVAILABLE";
            })
            .defaultIfEmpty("NOT_AVAILABLE");
}

3. 更新GraphQL Wiring配置

确保Wiring中使用响应式DataFetcher:

public List<TypeRuntimeWiring.Builder> buildWiring() {
    return Arrays.asList(newTypeWiring("Query")
        .dataFetcher("getUserName", getCurrentUser()));
}

关键注意事项

  • 禁止在响应式流中使用block()、toFuture().get()等同步阻塞方法,这会破坏上下文传递并引发线程问题。
  • ReactiveSecurityContextHolder的上下文仅存在于当前Reactor流中,脱离该流的代码无法读取到认证信息。
  • 若使用spring-boot-starter-graphql,需确保DataFetcher为响应式类型,才能自动继承WebFlux的上下文。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 17:13:28