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

Spring Cloud Gateway过滤器中消费WebClient API响应的实现问题

解决Spring Cloud Gateway中GatewayFilter内异步调用微服务并修改Exchange的问题

在Spring Cloud Gateway的响应式架构中,不能用.block()阻塞等待WebClient响应,必须通过响应式链式调用处理异步操作。你原来的代码中WebClient调用没有被订阅执行,也没有将其融入到filter的响应式链中,导致验证逻辑未生效。以下是修改后的完整实现:

@Component
@RequiredArgsConstructor
public class AuthFilter implements GatewayFilter {

    private static final String AUTHORIZATION = "Authorization";
    private final WebClient.Builder webClientBuilder;
    private final AuthenticationTokenService authenticationTokenService;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        final List<String> openEndpoints = List.of(
                "/api/v1/auth/login", "/api/v1/auth/refresh-token", "/api/v1/user/register",
                "/api/v1/server/health-check", "/api/v1/user/health-check"
        );
        final Predicate<ServerHttpRequest> isApiSecured = r -> openEndpoints.stream()
                .noneMatch(uri -> r.getURI().getPath().contains(uri));

        // 开放接口直接放行
        if (!isApiSecured.test(request)) {
            return chain.filter(exchange);
        }

        // 检查Authorization头是否存在
        if (!request.getHeaders().containsKey(AUTHORIZATION)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }

        try {
            final String authHeader = Objects.requireNonNull(request.getHeaders().get(AUTHORIZATION)).get(0);
            final String[] parts = authHeader.split(" ");
            if (parts.length != 2 || !"Bearer".equals(parts[0])) {
                throw new RuntimeException("Incorrect auth structure");
            }

            // 刷新token接口跳过用户验证
            if (request.getURI().getPath().endsWith("/refresh-token")) {
                return chain.filter(exchange);
            }

            final String authenticationToken = parts[1];
            final AuthenticationTokenDetails authenticationTokenDetails = authenticationTokenService
                    .parseAccessToken(authenticationToken);
            final Long id = authenticationTokenDetails.getUserId();
            final String uniqueId = authenticationTokenDetails.getUniqueId();

            // 异步调用PERSISTENCE-SERVICE验证用户合法性
            return webClientBuilder.build()
                    .get()
                    .uri("http://PERSISTENCE-SERVICE/api/v1/user/verify?id={id}&uniqueId={uniqueId}", id, uniqueId)
                    .retrieve()
                    .bodyToMono(Boolean.class)
                    .flatMap(isValid -> {
                        if (isValid) {
                            // 修改请求,添加用户信息头供下游服务直接使用
                            ServerHttpRequest modifiedRequest = request.mutate()
                                    .header("X-User-Id", id.toString())
                                    .header("X-User-UniqueId", uniqueId)
                                    .build();
                            // 创建新的Exchange实例(原Exchange为不可变对象)
                            ServerWebExchange modifiedExchange = exchange.mutate()
                                    .request(modifiedRequest)
                                    .build();
                            // 也可将验证结果存入Exchange属性,下游通过exchange.getAttribute获取
                            // modifiedExchange.getAttributes().put("USER_VALID", true);
                            return chain.filter(modifiedExchange);
                        } else {
                            response.setStatusCode(HttpStatus.FORBIDDEN);
                            return response.setComplete();
                        }
                    })
                    .onErrorResume(e -> {
                        // 处理调用失败场景(如服务不可达、超时等)
                        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                        return response.setComplete();
                    });

        } catch (Exception e) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            return response.setComplete();
        }
    }
}

关键修改说明

  • 响应式链式调用:将WebClient的异步调用融入filter的Mono<Void>响应链,通过flatMap处理验证结果,确保验证完成后再转发请求。
  • 安全URI构建:使用占位符方式拼接URI,避免字符串拼接导致的注入风险。
  • 不可变对象处理:ServerWebExchange是不可变对象,通过mutate()方法创建新实例来修改请求信息。
  • 异常覆盖:用onErrorResume捕获WebClient调用过程中的异常,返回对应HTTP状态码,避免请求无响应。
  • 逻辑简化:将开放接口判断提前,减少嵌套层级;刷新token接口单独处理,跳过用户验证流程。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 21:20:43