Spring Cloud Gateway动态添加请求头/查询参数失效问题排查
问题
我正在开发一个使用Spring Cloud Gateway作为网关、前端为Angular 2的应用,登录采用Keycloak SSO。登录后需将加密后的用户信息通过请求头或查询参数传递给前端,但编写的自定义过滤器代码未生效,前端无法获取该信息。
自定义过滤器代码如下:
@Component public class InterceptorFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<InterceptorFilterGatewayFilterFactory.Config> { public InterceptorFilterGatewayFilterFactory() { super(Config.class); } @Override public Config newConfig() { return new Config(); } public static class Config { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { ServerHttpRequest sr = exchange.getRequest(); Mono var10000 = exchange.getPrincipal().filter((principal) -> principal instanceof OAuth2AuthenticationToken) .map(p -> p).map((p) -> { LinkedHashSet<URI> attr = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); ServerHttpRequest request = exchange.getRequest(); // Here I try to add query parameter MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); queryParams.put("e", Collections.singletonList(toHexString(ja.toString().getBytes()))); URI newUri = UriComponentsBuilder.fromUri(request.getURI()) .replaceQueryParams(unmodifiableMultiValueMap(queryParams)) .build(true).toUri(); ServerHttpRequest updatedRequest = exchange.getRequest().mutate().uri(newUri) .build(); // Here I add header updatedRequest.mutate().header("e", toHexString(ja.toString().getBytes())).build(); return exchange.mutate().request(updatedRequest).build(); }).defaultIfEmpty(exchange).flatMap(chain::filter); } return var10000; }; }
application.yml中的路由配置如下:
- id: appDepartmentWise predicates: - Path=/app/*/sso_login - Method=GET,POST uri: http://localhost:9000/app/ filters: - PreserveHostHeader - RewritePath=/.*, /app/index.html - InterceptorFilter - AddRequestParameter=e,*
请问缺失了哪些配置?如何正确动态添加请求头或查询参数?
分析与解决方案
一、核心问题梳理
- Header添加无效:
ServerHttpRequest是不可变对象,调用mutate()会生成新实例,但你未将新实例赋值给变量,最终传递的请求仍是未添加Header的版本。 - 用户信息未正确获取:代码中直接使用未定义的
ja变量,没有从OAuth2AuthenticationToken中提取用户信息,会导致编译错误,过滤器无法正常运行。 - 参数被覆盖:路由配置里
AddRequestParameter=e,*在自定义过滤器之后执行,会把动态添加的e参数覆盖为*,前端拿到错误值。 - 过滤器顺序不合理:
RewritePath先执行会修改URI,后续修改查询参数的逻辑可能失效。
二、修正后的过滤器代码
@Component public class InterceptorFilterGatewayFilterFactory extends AbstractGatewayFilterFactory<InterceptorFilterGatewayFilterFactory.Config> { public InterceptorFilterGatewayFilterFactory() { super(Config.class); } @Override public Config newConfig() { return new Config(); } public static class Config { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> exchange.getPrincipal() .filter(principal -> principal instanceof OAuth2AuthenticationToken) .map(principal -> (OAuth2AuthenticationToken) principal) .map(oAuth2Token -> { // 从Token中提取用户信息,这里示例取用户名,替换为你实际需要加密的内容 String userInfo = oAuth2Token.getPrincipal().getName(); String encryptedUserInfo = toHexString(userInfo.getBytes()); // 1. 构造新的查询参数(保留原有参数,避免覆盖) ServerHttpRequest originalRequest = exchange.getRequest(); MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(originalRequest.getQueryParams()); queryParams.put("e", Collections.singletonList(encryptedUserInfo)); // 生成新URI URI newUri = UriComponentsBuilder.fromUri(originalRequest.getURI()) .replaceQueryParams(queryParams) .build(true) .toUri(); // 2. 链式修改请求URI和Header,确保新实例生效 ServerHttpRequest updatedRequest = originalRequest.mutate() .uri(newUri) .header("e", encryptedUserInfo) .build(); return exchange.mutate().request(updatedRequest).build(); }) .defaultIfEmpty(exchange) .flatMap(chain::filter); } // 补充你的十六进制转换实现,这里提供一个基础版本 private String toHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } }
三、路由配置修正
移除会覆盖参数的AddRequestParameter,调整过滤器顺序:
- id: appDepartmentWise predicates: - Path=/app/*/sso_login - Method=GET,POST uri: http://localhost:9000/app/ filters: - PreserveHostHeader - InterceptorFilter # 先执行自定义过滤器,再做路径重写 - RewritePath=/.*, /app/index.html
四、额外注意事项
- 跨域配置:如果前端是Angular,需在网关的CORS配置中添加
exposed-headers: e,确保前端能获取自定义请求头。 - 全局生效可选:若需所有路由都添加用户信息,可将类改为实现
GlobalFilter接口,无需在每个路由中单独配置。 - 传输安全:通过URL传递加密信息时,必须使用HTTPS避免明文泄露,同时确保加密算法的安全性。
内容的提问来源于stack exchange,提问作者user3458271
相关产品推荐
相关产品推荐

