简单来说,问题就是 Spring Cloud Gateway 如何动态添加请求参数。
Spring Cloud Gateway Add Request Parameter
- 查看官方文档,提供了下面的示例:
但是是在配置文件写明的,看起来好像只能是固定值。
- 在github上看到也有人提了类似问题,
但是实现的效果也跟配置文件差不多。
- 在stackoverflow上也查了类似回答:
stackoverflow.com/questions/6…
大概思路有了方向。
解决方案
====
在 Spring Cloud Gateway 源码上,发现了这两个类 AddRequestParameterGatewayFilterFactory
和 ModifyRequestBodyGatewayFilterFactory
代码内容如下:
AddRequestParameterGatewayFilterFactory
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.cloud.gateway.filter.factory;
import java.net.URI;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
public class AddRequestParameterGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
public AddRequestParameterGatewayFilterFactory() {
}
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
URI uri = exchange.getRequest().getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != ‘&’) {
query.append(‘&’);
}
}
query.append(config.getName());
query.append(‘=’);
query.append(config.getValue());
try {
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
} catch (RuntimeException var8) {
throw new IllegalStateException(“Invalid URI query: “” + query.toString() + “””);
}
};
}
}
复制代码
ModifyRequestBodyGatewayFilterFactory
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.cloud.gateway.filter.factory.rewrite;
import java.util.Map;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class ModifyRequestBodyGatewayFilterFactory extends AbstractGatewayFilterFactory<ModifyRequestBodyGatewayFilterFactory.Config> {
public ModifyRequestBodyGatewayFilterFactory() {
super(ModifyRequestBodyGatewayFilterFactory.Config.class);
}
/** @deprecated */
@Deprecated
public ModifyRequestBodyGatewayFilterFactory(ServerCodecConfigurer codecConfigurer) {
this();
}
public GatewayFilter apply(ModifyRequestBodyGatewayFilterFactory.Config config) {
return (exchange, chain) -> {
Class inClass = config.getInClass();
ServerRequest serverRequest = new DefaultServerRequest(exchange);
Mono<?> modifiedBody = serverRequest.bodyToMono(inClass).flatMap((o) -> {
return config.rewriteFunction.apply(exchange, o);
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, config.getOutClass());
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getRequest().getHeaders());
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
httpHeaders.set(“Transfer-Encoding”, “chunked”);
return httpHeaders;
}
public Flux getBody() {
return outputMessage.getBody();
}
};
return chain.filter(exchange.mutate().request(decorator).build());
}));
};
}
public static class Config {
private Class inClass;
private Class outClass;
private Map<String, Object> inHints;
private Map<String, Object> outHints;
private RewriteFunction rewriteFunction;
public Config() {
}
public Class getInClass() {
return this.inClass;
}
public ModifyRequestBodyGatewayFilterFactory.Config setInClass(Class inClass) {
this.inClass = inClass;
return this;
}
public Class getOutClass() {
return this.outClass;
}
public ModifyRequestBodyGatewayFilterFactory.Config setOutClass(Class outClass) {
this.outClass = outClass;
return this;
}
public Map<String, Object> getInHints() {
return this.inHints;
}
public ModifyRequestBodyGatewayFilterFactory.Config setInHints(Map<String, Object> inHints) {
this.inHints = inHints;
return this;
}
public Map<String, Object> getOutHints() {
return this.outHints;
}
public ModifyRequestBodyGatewayFilterFactory.Config setOutHints(Map<String, Object> outHints) {
this.outHints = outHints;
return this;
}
public RewriteFunction getRewriteFunction() {
return this.rewriteFunction;
}
public <T, R> ModifyRequestBodyGatewayFilterFactory.Config setRewriteFunction(Class inClass, Class outClass, RewriteFunction<T, R> rewriteFunction) {
this.setInClass(inClass);
this.setOutClass(outClass);
this.setRewriteFunction(rewriteFunction);
return this;
}
public ModifyRequestBodyGatewayFilterFactory.Config setRewriteFunction(RewriteFunction rewriteFunction) {
this.rewriteFunction = rewriteFunction;
return this;
}
}
}
复制代码
实际上,可以当作官方提供的参考示例。
照着类似内容,我们可以依样画葫芦,在自己的网关过滤器上实现添加参数的功能。
实现代码
====
鉴权过滤器主要处理流程
@Component
public class AuthFilter implements GlobalFilter, Ordered {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthFilter.class);
private static AntPathMatcher antPathMatcher;
static {
antPathMatcher = new AntPathMatcher();
}
@Override
public int getOrder() {
return FilterOrderConstant.getOrder(this.getClass().getName());
}
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
String url = uri.getPath();
String host = uri.getHost();
// 跳过不需要验证的路径
Stream skipAuthUrls = UrlConstant.skipAuthUrls.stream();
if(skipAuthUrls.anyMatch(path -> antPathMatcher.match(path, url))){
// 直接返回
ServerHttpRequest.Builder builder = request.mutate();
return chain.filter(exchange.mutate().request(builder.build()).build());
}
// 从请求头中取出token
String token = request.getHeaders().getFirst(“Authorization”);
// 取出token包含的身份信息
// 校验token逻辑不再阐述
BaseDTO baseDTO = getClaim(token);
if(null == baseDTO){
// 鉴权不通过,拿不到身份信息
return illegalResponse(exchange, “{“code”: “401”,“msg”: “unauthorized.”}”);
}
// 将现在的request,添加当前身份信息
ServerHttpRequest.Builder builder = request.mutate();
Stream addRequestParameterUrls = UrlConstant.addRequestParameterUrls.stream();
if (addRequestParameterUrls.anyMatch(path -> antPathMatcher.match(path, url))){
// 需要添加请求参数
if(request.getMethod() == HttpMethod.GET){
// get请求 处理参数
return addParameterForGetMethod(exchange, chain, uri, baseDTO, builder);
}
if(request.getMethod() == HttpMethod.POST){
// post请求 处理参数
MediaType contentType = request.getHeaders().getContentType();
if(MediaType.APPLICATION_JSON.equals(contentType)
|| MediaType.APPLICATION_JSON_UTF8.equals(contentType)){
// 请求内容为 application json
return addParameterForPostMethod(exchange, chain, baseDTO);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
分享
1、算法大厂——字节跳动面试题
2、2000页互联网Java面试题大全
3、高阶必备,算法学习
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)**
[外链图片转存中…(img-S1Lyd4dO-1712183791402)]
分享
1、算法大厂——字节跳动面试题
[外链图片转存中…(img-w1p4nIoD-1712183791402)]
2、2000页互联网Java面试题大全
[外链图片转存中…(img-BqzFrJW9-1712183791403)]
3、高阶必备,算法学习
[外链图片转存中…(img-YorRHaol-1712183791403)]
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算