首页>>后端>>Spring->Spring Cloud Gateway 如何动态添加请求参数

Spring Cloud Gateway 如何动态添加请求参数

时间:2023-11-30 本站 点击:0

背景介绍

项目使用的技术栈是Spring Cloud,有个功能需求是:

业务上,在Spring Cloud Gateway模块的服务已经可以获取到token,并且已实现鉴权通过后从token获取到身份信息;

现在希望把身份信息,填充到request参数里面(这里把多个数据封装成一个BaseDTO对象,用于扩展)。

后续处理具体业务的微服务模块,在controller层的方法传参,只要继承了BaseDTO对象,就可以直接获取到身份信息,用于业务逻辑处理。

问题描述

简单来说,问题就是 Spring Cloud Gateway 如何动态添加请求参数。

Spring Cloud Gateway Add Request Parameter

查看官方文档,提供了下面的示例:

https://docs.spring.io/spring-cloud-gateway/docs/2.2.9.RELEASE/reference/html/#gateway-request-predicates-factories

但是是在配置文件写明的,看起来好像只能是固定值。

在github上看到也有人提了类似问题,

https://github.com/spring-cloud/spring-cloud-gateway/issues/113

但是实现的效果也跟配置文件差不多。

在stackoverflow上也查了类似回答:

https://stackoverflow.com/questions/68741402/how-to-set-request-body-in-gatewayfilter-with-spring-cloud-gateway

大概思路有了方向。

解决方案

在 Spring Cloud Gateway 源码上,发现了这两个类 AddRequestParameterGatewayFilterFactoryModifyRequestBodyGatewayFilterFactory

代码内容如下:

AddRequestParameterGatewayFilterFactory

////Sourcecoderecreatedfroma.classfilebyIntelliJIDEA//(poweredbyFernFlowerdecompiler)//packageorg.springframework.cloud.gateway.filter.factory;importjava.net.URI;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory.NameValueConfig;importorg.springframework.http.server.reactive.ServerHttpRequest;importorg.springframework.util.StringUtils;importorg.springframework.web.util.UriComponentsBuilder;publicclassAddRequestParameterGatewayFilterFactoryextendsAbstractNameValueGatewayFilterFactory{publicAddRequestParameterGatewayFilterFactory(){}publicGatewayFilterapply(NameValueConfigconfig){return(exchange,chain)->{URIuri=exchange.getRequest().getURI();StringBuilderquery=newStringBuilder();StringoriginalQuery=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{URInewUri=UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(true).toUri();ServerHttpRequestrequest=exchange.getRequest().mutate().uri(newUri).build();returnchain.filter(exchange.mutate().request(request).build());}catch(RuntimeExceptionvar8){thrownewIllegalStateException("InvalidURIquery:\""+query.toString()+"\"");}};}}

ModifyRequestBodyGatewayFilterFactory

////Sourcecoderecreatedfroma.classfilebyIntelliJIDEA//(poweredbyFernFlowerdecompiler)//packageorg.springframework.cloud.gateway.filter.factory.rewrite;importjava.util.Map;importorg.springframework.cloud.gateway.filter.GatewayFilter;importorg.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;importorg.springframework.cloud.gateway.support.BodyInserterContext;importorg.springframework.cloud.gateway.support.CachedBodyOutputMessage;importorg.springframework.cloud.gateway.support.DefaultServerRequest;importorg.springframework.core.io.buffer.DataBuffer;importorg.springframework.http.HttpHeaders;importorg.springframework.http.codec.ServerCodecConfigurer;importorg.springframework.http.server.reactive.ServerHttpRequestDecorator;importorg.springframework.web.reactive.function.BodyInserter;importorg.springframework.web.reactive.function.BodyInserters;importorg.springframework.web.reactive.function.server.ServerRequest;importreactor.core.publisher.Flux;importreactor.core.publisher.Mono;publicclassModifyRequestBodyGatewayFilterFactoryextendsAbstractGatewayFilterFactory<ModifyRequestBodyGatewayFilterFactory.Config>{publicModifyRequestBodyGatewayFilterFactory(){super(ModifyRequestBodyGatewayFilterFactory.Config.class);}/**@deprecated*/@DeprecatedpublicModifyRequestBodyGatewayFilterFactory(ServerCodecConfigurercodecConfigurer){this();}publicGatewayFilterapply(ModifyRequestBodyGatewayFilterFactory.Configconfig){return(exchange,chain)->{ClassinClass=config.getInClass();ServerRequestserverRequest=newDefaultServerRequest(exchange);Mono<?>modifiedBody=serverRequest.bodyToMono(inClass).flatMap((o)->{returnconfig.rewriteFunction.apply(exchange,o);});BodyInserterbodyInserter=BodyInserters.fromPublisher(modifiedBody,config.getOutClass());CachedBodyOutputMessageoutputMessage=newCachedBodyOutputMessage(exchange,exchange.getRequest().getHeaders());returnbodyInserter.insert(outputMessage,newBodyInserterContext()).then(Mono.defer(()->{ServerHttpRequestDecoratordecorator=newServerHttpRequestDecorator(exchange.getRequest()){publicHttpHeadersgetHeaders(){HttpHeadershttpHeaders=newHttpHeaders();httpHeaders.putAll(super.getHeaders());httpHeaders.set("Transfer-Encoding","chunked");returnhttpHeaders;}publicFlux<DataBuffer>getBody(){returnoutputMessage.getBody();}};returnchain.filter(exchange.mutate().request(decorator).build());}));};}publicstaticclassConfig{privateClassinClass;privateClassoutClass;privateMap<String,Object>inHints;privateMap<String,Object>outHints;privateRewriteFunctionrewriteFunction;publicConfig(){}publicClassgetInClass(){returnthis.inClass;}publicModifyRequestBodyGatewayFilterFactory.ConfigsetInClass(ClassinClass){this.inClass=inClass;returnthis;}publicClassgetOutClass(){returnthis.outClass;}publicModifyRequestBodyGatewayFilterFactory.ConfigsetOutClass(ClassoutClass){this.outClass=outClass;returnthis;}publicMap<String,Object>getInHints(){returnthis.inHints;}publicModifyRequestBodyGatewayFilterFactory.ConfigsetInHints(Map<String,Object>inHints){this.inHints=inHints;returnthis;}publicMap<String,Object>getOutHints(){returnthis.outHints;}publicModifyRequestBodyGatewayFilterFactory.ConfigsetOutHints(Map<String,Object>outHints){this.outHints=outHints;returnthis;}publicRewriteFunctiongetRewriteFunction(){returnthis.rewriteFunction;}public<T,R>ModifyRequestBodyGatewayFilterFactory.ConfigsetRewriteFunction(Class<T>inClass,Class<R>outClass,RewriteFunction<T,R>rewriteFunction){this.setInClass(inClass);this.setOutClass(outClass);this.setRewriteFunction(rewriteFunction);returnthis;}publicModifyRequestBodyGatewayFilterFactory.ConfigsetRewriteFunction(RewriteFunctionrewriteFunction){this.rewriteFunction=rewriteFunction;returnthis;}}}

实际上,可以当作官方提供的参考示例。

照着类似内容,我们可以依样画葫芦,在自己的网关过滤器上实现添加参数的功能。

实现代码

鉴权过滤器主要处理流程

@ComponentpublicclassAuthFilterimplementsGlobalFilter,Ordered{privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(AuthFilter.class);privatestaticAntPathMatcherantPathMatcher;static{antPathMatcher=newAntPathMatcher();}@OverridepublicintgetOrder(){returnFilterOrderConstant.getOrder(this.getClass().getName());}@OverridepublicMono<Void>filter(ServerWebExchangeexchange,GatewayFilterChainchain){ServerHttpRequestrequest=exchange.getRequest();URIuri=request.getURI();Stringurl=uri.getPath();Stringhost=uri.getHost();//跳过不需要验证的路径Stream<String>skipAuthUrls=UrlConstant.skipAuthUrls.stream();if(skipAuthUrls.anyMatch(path->antPathMatcher.match(path,url))){//直接返回ServerHttpRequest.Builderbuilder=request.mutate();returnchain.filter(exchange.mutate().request(builder.build()).build());}//从请求头中取出tokenStringtoken=request.getHeaders().getFirst("Authorization");//取出token包含的身份信息//校验token逻辑不再阐述BaseDTObaseDTO=getClaim(token);if(null==baseDTO){//鉴权不通过,拿不到身份信息returnillegalResponse(exchange,"{\"code\":\"401\",\"msg\":\"unauthorized.\"}");}//将现在的request,添加当前身份信息ServerHttpRequest.Builderbuilder=request.mutate();Stream<String>addRequestParameterUrls=UrlConstant.addRequestParameterUrls.stream();if(addRequestParameterUrls.anyMatch(path->antPathMatcher.match(path,url))){//需要添加请求参数if(request.getMethod()==HttpMethod.GET){//get请求处理参数returnaddParameterForGetMethod(exchange,chain,uri,baseDTO,builder);}if(request.getMethod()==HttpMethod.POST){//post请求处理参数MediaTypecontentType=request.getHeaders().getContentType();if(MediaType.APPLICATION_JSON.equals(contentType)||MediaType.APPLICATION_JSON_UTF8.equals(contentType)){//请求内容为applicationjsonreturnaddParameterForPostMethod(exchange,chain,baseDTO);}if(MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)){//请求内容为formdatareturnaddParameterForFormData(exchange,chain,baseDTO,builder);}}if(request.getMethod()==HttpMethod.PUT){//put请求处理参数//走post请求流程returnaddParameterForPostMethod(exchange,chain,baseDTO);}if(request.getMethod()==HttpMethod.DELETE){//delete请求处理参数//走get请求流程returnaddParameterForGetMethod(exchange,chain,uri,baseDTO,builder);}}//当前过滤器filter执行结束returnchain.filter(exchange.mutate().request(builder.build()).build());}}

Get请求 添加参数

/***get请求,添加参数*@paramexchange*@paramchain*@paramuri*@parambaseDTO*@parambuilder*@return*/privateMono<Void>addParameterForGetMethod(ServerWebExchangeexchange,GatewayFilterChainchain,URIuri,BaseDTObaseDTO,ServerHttpRequest.Builderbuilder){StringBuilderquery=newStringBuilder();StringoriginalQuery=uri.getQuery();if(StringUtils.hasText(originalQuery)){query.append(originalQuery);if(originalQuery.charAt(originalQuery.length()-1)!='&'){query.append('&');}}query.append("userId").append("=").append(baseDTO.getUserId()).append("&").append("userName").append("=").append(baseDTO.getUserName());try{URInewUri=UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build().encode().toUri();ServerHttpRequestrequest=exchange.getRequest().mutate().uri(newUri).build();returnchain.filter(exchange.mutate().request(request).build());}catch(Exceptione){LOGGER.error("InvalidURIquery:"+query.toString(),e);//当前过滤器filter执行结束returnchain.filter(exchange.mutate().request(builder.build()).build());}}

Post请求 添加参数

请求内容为 application json

/***post请求,添加参数*@paramexchange*@paramchain*@parambaseDTO*@return*/privateMono<Void>addParameterForPostMethod(ServerWebExchangeexchange,GatewayFilterChainchain,BaseDTObaseDTO){ServerRequestserverRequest=newDefaultServerRequest(exchange);AtomicBooleanflag=newAtomicBoolean(false);Mono<String>modifiedBody=serverRequest.bodyToMono(String.class).flatMap((o)->{if(o.startsWith("[")){//body内容为数组,直接返回returnMono.just(o);}ObjectMapperobjectMapper=newObjectMapper();try{Mapmap=objectMapper.readValue(o,Map.class);map.put("userId",baseDTO.getUserId());map.put("userName",baseDTO.getUserName());Stringjson=objectMapper.writeValueAsString(map);LOGGER.info("addParameterForPostMethod->json={}",json);returnMono.just(json);}catch(Exceptione){e.printStackTrace();returnMono.just(o);}});BodyInserterbodyInserter=BodyInserters.fromPublisher(modifiedBody,String.class);CachedBodyOutputMessageoutputMessage=newCachedBodyOutputMessage(exchange,exchange.getRequest().getHeaders());returnbodyInserter.insert(outputMessage,newBodyInserterContext()).then(Mono.defer(()->{ServerHttpRequestDecoratordecorator=newServerHttpRequestDecorator(exchange.getRequest()){publicHttpHeadersgetHeaders(){HttpHeadershttpHeaders=newHttpHeaders();httpHeaders.putAll(super.getHeaders());httpHeaders.set("Transfer-Encoding","chunked");returnhttpHeaders;}publicFlux<DataBuffer>getBody(){returnoutputMessage.getBody();}};returnchain.filter(exchange.mutate().request(decorator).build());}));}

请求内容为 form data

/***post请求,formdata添加参数*@paramexchange*@paramchain*@parambaseDTO*@parambuilder*@return*/privateMono<Void>addParameterForFormData(ServerWebExchangeexchange,GatewayFilterChainchain,BaseDTObaseDTO,ServerHttpRequest.Builderbuilder){builder.header("userId",String.valueOf(baseDTO.getUserId()));try{builder.header("userName",URLEncoder.encode(String.valueOf(baseDTO.getUserName()),"UTF-8"));}catch(UnsupportedEncodingExceptione){builder.header("userName",String.valueOf(baseDTO.getUserName()));}ServerHttpRequestserverHttpRequest=builder.build();HttpHeadersheaders=serverHttpRequest.getHeaders();returnchain.filter(exchange.mutate().request(serverHttpRequest).build());}

返回数据处理

/***返回消息*@paramexchange*@paramdata*@return*/privateMono<Void>illegalResponse(ServerWebExchangeexchange,Stringdata){ServerHttpResponseoriginalResponse=exchange.getResponse();originalResponse.setStatusCode(HttpStatus.OK);originalResponse.getHeaders().add(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON_UTF8_VALUE);byte[]response=data.getBytes(StandardCharsets.UTF_8);DataBufferbuffer=originalResponse.bufferFactory().wrap(response);returnoriginalResponse.writeWith(Flux.just(buffer));}

最终效果

上面描述,已实现将userIduserName两个属性,写入到request参数中。

在具体业务处理的服务模块,controller层的传参,只要继承包含userId和userName两个属性的BaseDTO类,就可以拿到该信息,用于实际的业务流程。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/Spring/4440.html