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

Spring Boot Cucumber测试中Reactive Security Context始终为空问题

Cucumber测试中ReactiveSecurityContextHolder获取不到上下文的问题排查

问题描述

我使用Spring Boot和Reactor开发服务,采用Cucumber进行测试时遇到以下问题:
在步骤定义的userIsAuthenticated方法中,调用ReactiveSecurityContextHolder.getContext()始终返回null,无法设置认证用户。但在常规Spring Boot测试中,通过@WithMockUser注解可正常获取安全上下文,且服务已正确注入到步骤定义类中。

核心原因

Cucumber的测试上下文初始化逻辑和Spring Boot常规测试存在差异:

  1. @WithMockUser这类Spring Security测试注解无法直接在Cucumber步骤类中生效——因为Cucumber不会触发Spring测试框架的TestExecutionListener,而这类注解的生效依赖该监听器。
  2. Reactive Security的上下文依赖Reactor线程的Context绑定,Cucumber默认不会自动完成这个绑定操作。

缺失的配置与解决步骤

1. 引入必要的Cucumber依赖

首先确保POM中添加衔接Cucumber与Spring Security、Reactor的专用依赖,版本需与Cucumber核心版本保持一致:

<!-- Cucumber Spring Security 衔接依赖 -->
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-spring-security</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
</dependency>

<!-- Cucumber Reactor 支持依赖(步骤返回Mono/Flux时需要) -->
<dependency>
    <groupId>io.cucumber</groupId>
    <artifactId>cucumber-reactor</artifactId>
    <version>${cucumber.version}</version>
    <scope>test</scope>
</dependency>

2. 配置Cucumber测试上下文的Reactive Security支持

在你的Cucumber配置类(带有@CucumberContextConfiguration注解的类)上,添加Reactive Security的启用注解和测试配置导入:

import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.test.context.support.ReactiveSpringSecurityTestConfiguration;

@CucumberContextConfiguration
@SpringBootTest
@EnableWebFluxSecurity
@Import(ReactiveSpringSecurityTestConfiguration.class)
public class CucumberSpringConfig {
}

3. 手动在步骤中绑定Reactive安全上下文

由于@WithMockUser无法直接生效,需要在userIsAuthenticated步骤方法里手动构建认证对象,并绑定到Reactor的Context中,同时确保步骤方法返回Mono<Void>以传递上下文:

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import reactor.core.publisher.Mono;
import reactor.util.context.Context;

@Given("用户已认证")
public Mono<Void> userIsAuthenticated() {
    // 构建模拟认证用户
    UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
            "testUser",
            null,
            List.of(new SimpleGrantedAuthority("ROLE_USER"))
    );
    SecurityContext securityContext = new SecurityContextImpl(authToken);
    
    // 将安全上下文绑定到Reactor Context,确保后续服务调用能获取到
    return Mono.deferContextual(currentCtx -> {
        Context updatedCtx = currentCtx.put(ReactiveSecurityContextHolder.CONTEXT_KEY, securityContext);
        return Mono.empty().contextWrite(updatedCtx);
    });
}

4. 避免错误的同步获取方式

不要在非Reactive的步骤方法中直接调用ReactiveSecurityContextHolder.getContext().block(),这种同步阻塞操作会因为上下文还未绑定而返回null,必须通过Reactive流的上下文传递机制处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:25:35