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

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,*

请问缺失了哪些配置?如何正确动态添加请求头或查询参数?


分析与解决方案

一、核心问题梳理

  1. Header添加无效:ServerHttpRequest是不可变对象,调用mutate()会生成新实例,但你未将新实例赋值给变量,最终传递的请求仍是未添加Header的版本。
  2. 用户信息未正确获取:代码中直接使用未定义的ja变量,没有从OAuth2AuthenticationToken中提取用户信息,会导致编译错误,过滤器无法正常运行。
  3. 参数被覆盖:路由配置里AddRequestParameter=e,*在自定义过滤器之后执行,会把动态添加的e参数覆盖为*,前端拿到错误值。
  4. 过滤器顺序不合理: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 23:05:24