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

如何为WebFlux Security配置两步认证过滤器链

Spring WebFlux 多认证链路解耦实现方案

需求说明

需要支持两类并行生效的认证场景,服务端校验时任意一种认证通过即可放行请求:

  • 信任系统场景:令牌由预配置的过滤器自动注入请求上下文
  • 类Postman测试场景:从内部应用签发令牌用于接口结果验证,该场景仅允许在企业内网环境下生效

初始演示实现代码如下:

RemoteAddressSource remoteAddressSource = new WebFluxRemoteAddressSource(exchange);
return validateNotBlank(token)
        .switchIfEmpty(Mono.defer(() -> iafTokenValidator.validateToken(remoteAddressSource)
                .onErrorResume(AuthenticationFailedException.class, e -> tfTokenValidator.validateToken(remoteAddressSource))))
        .onErrorResume(AuthenticationFailedException.class, e -> sendErrorResponse(e, exchange))
        .switchIfEmpty(chain);

现存问题

  • 两类认证逻辑强耦合,没有拆分为独立校验单元,后续扩展、维护成本高
  • 认证失败时返回的错误信息始终为第二种认证方式的报错,无法准确反馈高优先级认证校验失败的真实原因
  • 期望通过标准过滤器链配置实现需求,但未找到Spring WebFlux生态下的适配实现路径

落地实现

1. 抽象统一认证器接口

统一所有认证方式的执行规范,支持按优先级排序执行:

/**
 * WebFlux响应式认证器统一接口
 */
public interface ReactiveAuthenticator {
    /**
     * 执行认证校验
     * @return 认证通过返回认证主体信息,失败抛出AuthenticationFailedException
     */
    Mono<Authentication> authenticate(ServerWebExchange exchange, RemoteAddressSource remoteAddressSource);

    /**
     * 认证器执行优先级,数值越小优先级越高
     */
    int getOrder();
}

2. 拆分独立认证实现类

将两类认证逻辑拆分为完全独立的实现类,互不依赖:

/**
 * 信任系统场景认证器,优先级最高
 */
@Component
public class IafSystemAuthenticator implements ReactiveAuthenticator {
    private final IafTokenValidator iafTokenValidator;

    public IafSystemAuthenticator(IafTokenValidator iafTokenValidator) {
        this.iafTokenValidator = iafTokenValidator;
    }

    @Override
    public Mono<Authentication> authenticate(ServerWebExchange exchange, RemoteAddressSource remoteAddressSource) {
        String injectedToken = exchange.getRequest().getHeaders().getFirst("X-Injected-Auth-Token");
        return validateNotBlank(injectedToken)
                .then(iafTokenValidator.validateToken(remoteAddressSource));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
/**
 * 内网测试场景认证器,优先级次于信任系统认证
 */
@Component
public class IntranetTestAuthenticator implements ReactiveAuthenticator {
    private final TfTokenValidator tfTokenValidator;
    private final IntranetRangeMatcher intranetRangeMatcher;

    public IntranetTestAuthenticator(TfTokenValidator tfTokenValidator, IntranetRangeMatcher intranetRangeMatcher) {
        this.tfTokenValidator = tfTokenValidator;
        this.intranetRangeMatcher = intranetRangeMatcher;
    }

    @Override
    public Mono<Authentication> authenticate(ServerWebExchange exchange, RemoteAddressSource remoteAddressSource) {
        // 前置校验:非内网环境直接拦截,不进入令牌校验逻辑
        return Mono.just(remoteAddressSource.getRemoteAddress())
                .filter(intranetRangeMatcher::isInRange)
                .switchIfEmpty(Mono.error(new AuthenticationFailedException("测试场景认证仅允许在内网环境使用")))
                .then(tfTokenValidator.validateToken(remoteAddressSource));
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

3. 实现统一认证过滤器

按优先级顺序依次执行认证逻辑,收集所有认证阶段的异常,所有认证方式均失败时返回对应场景的错误信息,解决错误透传问题:

@Component
public class MultiAuthWebFilter implements WebFilter {
    private final List<ReactiveAuthenticator> authenticators;
    private final ObjectMapper objectMapper;

    // Spring会自动注入所有ReactiveAuthenticator实现,并按@Order注解排序
    public MultiAuthWebFilter(List<ReactiveAuthenticator> authenticators, ObjectMapper objectMapper) {
        // 兜底按getOrder返回值排序,避免注入顺序异常
        this.authenticators = authenticators.stream()
                .sorted(Comparator.comparingInt(ReactiveAuthenticator::getOrder))
                .toList();
        this.objectMapper = objectMapper;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        RemoteAddressSource remoteAddressSource = new WebFluxRemoteAddressSource(exchange);
        List<AuthenticationFailedException> authErrors = new ArrayList<>();

        // 按优先级串行尝试认证
        Mono<Authentication> authExecuteChain = authenticators.stream()
                .reduce(
                        Mono.<Authentication>empty(),
                        (currentAuthMono, authenticator) -> currentAuthMono
                                .switchIfEmpty(Mono.defer(() -> authenticator.authenticate(exchange, remoteAddressSource)
                                        .onErrorResume(AuthenticationFailedException.class, e -> {
                                            authErrors.add(e);
                                            return Mono.empty();
                                        }))),
                        Mono::or
                );

        return authExecuteChain
                .flatMap(authInfo -> {
                    // 认证通过,将认证信息写入上下文后放行
                    exchange.getAttributes().put(WebFilterExchange.AUTHENTICATION_ATTR, authInfo);
                    return chain.filter(exchange);
                })
                .switchIfEmpty(Mono.defer(() -> {
                    // 所有认证均失败,返回最高优先级认证场景的错误(可根据业务规则调整错误选择逻辑)
                    AuthenticationFailedException targetError = authErrors.get(0);
                    return writeUnauthorizedResponse(targetError, exchange);
                }));
    }

    private Mono<Void> writeUnauthorizedResponse(AuthenticationFailedException ex, ServerWebExchange exchange) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        Map<String, Object> respBody = Map.of(
                "code", HttpStatus.UNAUTHORIZED.value(),
                "msg", ex.getMessage()
        );
        try {
            byte[] respBytes = objectMapper.writeValueAsBytes(respBody);
            DataBuffer buffer = response.bufferFactory().wrap(respBytes);
            return response.writeWith(Mono.just(buffer));
        } catch (JsonProcessingException e) {
            return response.setComplete();
        }
    }
}

配置说明

  • 将MultiAuthWebFilter注册到Spring WebFlux过滤器链,顺序放在所有业务接口拦截逻辑之前即可,不需要修改原有TokenValidator的内部实现
  • 后续新增认证方式时,仅需要新增ReactiveAuthenticator接口的实现类,配置对应优先级即可,不需要修改现有认证逻辑,符合开闭原则
  • 错误返回逻辑可根据业务规则调整:比如识别到请求携带信任系统注入的专属请求头时,直接返回信任系统认证的错误;识别为内网测试请求时返回测试场景错误,当前默认返回最高优先级认证的错误,已解决原有实现始终返回最后一个认证器错误的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 21:36:26