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

Webflux中HttpSecurity用access配置时@WithMockUser失效,如何测试控制器?

解决Webflux自定义ReactiveAuthorizationManager的测试问题

我明白你遇到的痛点——@WithMockUser确实在这种自定义ReactiveAuthorizationManager的场景下不好使,因为它默认把Principal设成字符串,而你的代码里直接强转成CustomUserDetails,肯定拿不到正确的权限判断结果。下面给你一套可行的测试方案:

1. 先创建Fake CustomUserDetails实现

首先,你需要一个伪造的CustomUserDetails实例,用来模拟不同权限的用户。根据你的hasAuthorityForAnyBrand方法逻辑,我们可以写一个测试专用的实现:

public class FakeCustomUserDetails extends CustomUserDetails {
    private final boolean hasRequiredAuthority;

    // 构造方法根据你的CustomUserDetails实际字段调整,重点模拟权限判断逻辑
    public FakeCustomUserDetails(String username, boolean hasRequiredAuthority) {
        super(username, "", Collections.emptyList()); // 填充父类需要的必填字段
        this.hasRequiredAuthority = hasRequiredAuthority;
    }

    @Override
    public boolean hasAuthorityForAnyBrand(Module module, Action action) {
        // 测试时直接返回预设结果,不用走真实业务逻辑
        return hasRequiredAuthority;
    }

    // 如果你的CustomUserDetails还有其他抽象方法,按需模拟实现即可
}

2. 在测试中手动构建Authentication并绑定到SecurityContext

因为Webflux是响应式框架,我们需要用ReactiveSecurityContextHolder把伪造的Authentication注入到请求上下文里。这里可以写一个工具方法复用逻辑:

private void setFakeUserInSecurityContext(boolean hasAuthority) {
    FakeCustomUserDetails fakeUser = new FakeCustomUserDetails("test-user", hasAuthority);
    Authentication authentication = new UsernamePasswordAuthenticationToken(
        fakeUser,
        null, // 测试场景下凭证可以设为null
        Collections.emptyList() // 权限列表可根据你的测试需求调整
    );
    authentication.setAuthenticated(true); // 标记为已认证状态

    // 将Authentication放入SecurityContext,绑定到当前响应式上下文
    ReactiveSecurityContextHolder.getContext()
        .contextWrite(securityContext -> securityContext.setAuthentication(authentication))
        .block();
}

3. 编写WebTestClient测试用例

Webflux的控制器测试通常用WebTestClient,结合上面的工具方法,就能测试不同权限用户的访问情况:

@WebFluxTest(YourTargetApiController.class)
@Import({HttpSecurityConfig.class, ModuleActionAuthorization.class}) // 导入你的安全配置和授权管理器
class YourTargetApiControllerTest {

    @Autowired
    private WebTestClient webTestClient;

    @Test
    void whenUserHasAuthority_thenCanAccessApi() {
        // 设置有权限的伪造用户
        setFakeUserInSecurityContext(true);

        webTestClient.get()
            .uri("/api/your-test-endpoint")
            .exchange()
            .expectStatus().isOk();
    }

    @Test
    void whenUserHasNoAuthority_thenIsForbidden() {
        // 设置无权限的伪造用户
        setFakeUserInSecurityContext(false);

        webTestClient.get()
            .uri("/api/your-test-endpoint")
            .exchange()
            .expectStatus().isForbidden();
    }
}

进阶:自定义测试注解(可选)

如果多个测试类都需要用到这个逻辑,可以自定义一个注解+处理器,简化测试代码。比如:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WithFakeUser {
    boolean hasAuthority() default true;
}

然后写一个TestExecutionListener或者结合@BeforeEach+反射来处理这个注解,自动帮你设置Fake用户,进一步减少重复代码。


补充说明下为什么@WithMockUser失效:它默认创建的UsernamePasswordAuthenticationToken里的Principal是字符串类型(比如你设置的username),而你的ModuleActionAuthorization里直接把a.getPrincipal()强转成CustomUserDetails,这会导致类型转换异常,或者根本触发不了正确的权限判断逻辑。所以必须手动构建带CustomUserDetails实例的Authentication。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:12:43