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
相关产品推荐
相关产品推荐

