Zuul 1.x 升级 springcloud gateway 2.x 遇到的一点问题

大家好,我是烤鸭:
今天分享 Zuul 1.x 升级 springcloud gateway 2.x 遇到的一点问题。

介绍

zuul 和springcloud gateway 都是比较优秀的网关,而 zuul 1.x 采用的是 servlet 模型,gate 采用的是 reactor模型,效率和资源上 gateway 要优秀一些。

zuul 和 springcloud 在 filter 架构上类似,都提供了基类 ZuulFilter 和 GlobalFilter,只要继承/实现基类就可以自定义拦截器,并且有排序。

升级场景

由于原来的网关项目并没有完全脱离业务场景(使用了部分zuul的拦截器和大量的自定义拦截器,白名单、签名校验等等),所以改造起来是比较麻烦的。

在全局参数管理上,由于是单线程模型,zuul1 采用 TheadLocal 维护的 RequestContext,而 gateway 需要使用 exchange 传递上下文参数。

zuul的拦截器改造

原网关的配置有 :

zuul.ignored-services=*
zuul.ignoredPatterns=/abc,/abd
zuul.sensitiveHeaders=key1

zuul.ignored-services=*, zuul有默认的隐射机制 * 表示禁用默认路由,外界无法访问未配置路由的服务
zuul.ignoredPatterns=/abc/aaa,/abd ,如果路由配置了 /abc/**,那么abc/aaa会被过滤,其他访问正常
zuul.sensitiveHeaders=key1,忽略某个敏感的请求头(不向下游传递)

  • zuul.ignored-services

springcloud 默认是不会转发未配置的路由,所以第一个不需要考虑。

  • zuul.ignoredPatterns=/abc/aaa,/abd

过滤请求路径,gateway没有现成的filter,所以需要自己写一个,我用的 spring security

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>

WebSecurityConfig

package test.gateway.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.stereotype.Component;@RefreshScope
@Configuration
public class WebSecurityConfig {/*** 网关入口黑名单*/@Value("${gateway.routes.black:/abc/aaa,/abd}")private String[] blacklist;/*** 网关入口白名单*/@Value("${gateway.routes.white:/*}")private String[] whitelist;@Beanpublic SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {http.csrf().disable().authorizeExchange()//需要授权使用的特殊接口,黑白名单顺序很重要,需要授权的放上面.pathMatchers(blacklist).authenticated()//无需进行权限过滤的请求路径.pathMatchers(whitelist).permitAll().anyExchange().authenticated().and().httpBasic().and().formLogin();return http.build();}}
  • zuul.sensitiveHeaders

这个其实gateway 有现成的filter,只要配置下就可以了,RemoveRequestHeaderGatewayFilterFactory

调下游服务之前会把这个header去掉,但是我这个场景不符合,只能手写一个filter。

原因是要过滤前端传过来的某个key(重要的头参数),正常这个参数会从token中解密获得,如果解密成功,会覆盖前端传过来的(使用的是 RequestContext.addZuulRequestHeader,当然前端也可能没传)。怕的是接口被盗刷,随意传重要的头参数,而不会走解密(不是所以接口都需要解密),所以需要在前端传了的情况下,remove掉。
RemoveRequestHeaderGatewayFilterFactory 只是在调下游服务前过滤,会过滤正常的(解密成功覆盖的)。所以需要手写filter,顺序在 解密的filter 之前执行。

SensitiveHeadersFilter

package test.gateway.filters.pre;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class SensitiveHeadersFilter implements GlobalFilter, Ordered {private static Logger logger = LoggerFactory.getLogger(SensitiveHeadersFilter.class);/*** 过滤请求头*/@Value("${gateway.sensitive.headers:key}")private String[] sensitiveHeaders;/*** @param* @return int* @Author * @Description 只要在 DecryptFilter 之前执行就可以,目的是移除配置的请求头* @Date 2021/2/7 16:35**/@Overridepublic int getOrder() {return HIGHEST_PRECEDENCE + 1000;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {HttpHeaders headers = exchange.getRequest().getHeaders();if (headers != null) {for (int i = 0; i < sensitiveHeaders.length; i++) {if(headers.containsKey(sensitiveHeaders[i])){headers.remove(sensitiveHeaders[i]);}}}return chain.filter(exchange);}
}

路由配置

改造如下:

#zuul.routes.aaa.path=/aaa
#zuul.routes.aaa.url=http://www.test.com/
spring.cloud.gateway.routes[0].id=aaa
spring.cloud.gateway.routes[0].filters[0].name=StripPrefix
spring.cloud.gateway.routes[0].filters[0].args.parts=1
spring.cloud.gateway.routes[0].predicates[0].name=Path
spring.cloud.gateway.routes[0].predicates[0].args[pattern]=/aaa
spring.cloud.gateway.routes[0].uri=http://www.test.com/#zuul.routes.bbb.path=/bbb/**
#zuul.routes.bbb.serviceId=bbb
spring.cloud.gateway.routes[1].id=bbb
spring.cloud.gateway.routes[1].filters[0].name=StripPrefix
spring.cloud.gateway.routes[1].filters[0].args.parts=1
spring.cloud.gateway.routes[1].predicates[0].name=Path
spring.cloud.gateway.routes[1].predicates[0].args[pattern]=/bbb/**
spring.cloud.gateway.routes[1].uri=lb://bbb

其他逻辑修改

由于模型不同,获取参数的方式等等都不一样,除了代码上兼容,逻辑上还需要再测试。

就比如如果zuul想获取post请求body体的参数:

可以直接使用 (RequestContext)ctx.getRequest().getParameterMap()
调用的是 com.netflix.zuul.http.HttpServletRequestWrapper.parseRequest() 可以直接解析参数后返回map,而 gateway 里没有类似的方法的,还需要考虑到流读取后再写回的问题。

类似场景可以参考下面这个过滤器,在后续需要用到参数的地方 可以通过 exchange.getAttributes(“POST_BODY”) 获取

HttpRequestGlobalFilter

package test.filters.pre;import java.nio.charset.StandardCharsets;import test.ZuulFilterAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;public class HttpRequestGlobalFilter extends ZuulFilterAdapter {private static Logger logger = LoggerFactory.getLogger(HttpRequestGlobalFilter.class);@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();String requestUrl = request.getPath().toString();String requestMethod = request.getMethodValue();if (HttpMethod.POST.toString().equals(requestMethod) || HttpMethod.PUT.toString().equals(requestMethod)) {return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {byte[] bytes = new byte[dataBuffer.readableByteCount()];dataBuffer.read(bytes);String postRequestBodyStr = new String(bytes, StandardCharsets.UTF_8);String contentType = request.getHeaders().getFirst("Content-Type");if (requestUrl.contains("/web/api/file") || contentType.startsWith("multipart/form-data")) {logger.info("\n 请求url:`{}` \n 请求类型:{} \n 文件上传", requestUrl, requestMethod);} else {logger.info("\n 请求url:`{}` \n 请求类型:{} \n 请求参数:{}", requestUrl, requestMethod, postRequestBodyStr);}exchange.getAttributes().put("POST_BODY", postRequestBodyStr);DataBufferUtils.release(dataBuffer);Flux<DataBuffer> cachedFlux = Flux.defer(() -> {DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);return Mono.just(buffer);});// 下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic Flux<DataBuffer> getBody() {return cachedFlux;}};// 封装request,传给下一级return chain.filter(exchange.mutate().request(mutatedRequest).build());});} else if (HttpMethod.GET.toString().equals(requestMethod)|| HttpMethod.DELETE.toString().equals(requestMethod)){MultiValueMap<String, String> getRequestParams = request.getQueryParams();logger.debug("\n 请求url:`{}` \n 请求类型:{} \n 请求参数:{}", requestUrl, requestMethod, getRequestParams);return chain.filter(exchange);}return chain.filter(exchange);}
}

总结

目前升级还没有完成,坑比想象中的要多…
除了框架本身的问题,还有代码年久失修、业务逻辑等等一系列的问题
代码重构 任重道远啊

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

render_template 网页模板

模板简单介绍&#xff1a; 视图函数&#xff1a;视图函数就是装饰器所装饰的方法&#xff0c;视图函数的主要作用是生成请求的响应&#xff0c;这是最简单的请求。实际上&#xff0c;视图函数有两个作用&#xff1a;处理业务逻辑和返回响应内容。在大型应用中&#xff0c;把业务…

[css] 怎么才能让图文不可复制?

[css] 怎么才能让图文不可复制&#xff1f; -webkit-user-select: none; -ms-user-select: none; -moz-user-select: none; -khtml-user-select: none; user-select: none;个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&#xff0c; 但坚持一定很酷…

nacos配置刷新失败导致的cpu上升和频繁重启,nacos配置中心源码解析

大家好&#xff0c;我是烤鸭&#xff1a; nacos 版本 1.3.2&#xff0c;先说下结论&#xff0c;频繁重启的原因确实没有找到&#xff0c;跟nacos有关&#xff0c;日志没有保留多少&#xff0c;只能从源码找下头绪(出问题的版本 server用的是 nacos 1.1&#xff0c;nacos-client…

nova— 计算服务

一、nova介绍&#xff1a; Nova 是 OpenStack 最核心的服务&#xff0c;负责维护和管理云环境的计算资源。OpenStack 作为 IaaS 的云操作系统&#xff0c;虚拟机生命周期管理也就是通过 Nova 来实现的。用途与功能 :1) 实例生命周期管理2) 管理计算资源3) 网络和认证管理4)REST…

[css] 写出你知道的CSS水平和垂直居中的方法

[css] 写出你知道的CSS水平和垂直居中的方法 flex布局水平垂直居中:<!-- html --> <div class"outer"><div class"inner"></div> </div>/*css*/ .outer{display:flex;width:200px;height:200px;border:1px solid red; } .…

springcloud gateway 自定义 accesslog elk

大家好&#xff0c;我是烤鸭&#xff1a; ​ 最近用 springcloud gateway 时&#xff0c;想使用类似 logback-access的功能&#xff0c;用来做数据统计和图表绘制等等&#xff0c;发现没有类似的功能&#xff0c;只能自己开发了。 环境&#xff1a; <dependency><gr…

第二阶段团队绩效考核报告

团队绩效考核 基于各种客观问题本次绩效考核采用和第一次冲刺不一样的标准&#xff0c;根据团队贡献事实打分如下 组员打分&#xff1a; 郭良 &#xff08;9.0&#xff09; 赵承龙 &#xff08;5.5&#xff09; &#xff08;根据组内之前定下的打分细则和本期冲刺过程的事实…

[css] 实现单行文本居中和多行文本左对齐并超出显示“...“

[css] 实现单行文本居中和多行文本左对齐并超出显示"…" .one {text-align: center }.multi {overflow: hiddentext-overflow: ellipsisdisplay: -webkit-box-webkit-line-clamp: 3-webkit-box-orient: vertical }可惜多行文本省略, 有严重的兼容性问题个人简介 我…

jenkins发布docker项目 harbor

大家好&#xff0c;我是烤鸭&#xff1a; ​ jenkins 部署k8s 项目还是比较流畅的&#xff0c;本身建立多流水线项目&#xff0c;在项目中添加jenkinsfile就好了&#xff0c;镜像需要额外的参数&#xff0c;还可以添加dokcerfile文件。由于我现在的问题是不能够修改原有的项…

[css] 不使用border画出1px高的线,在不同浏览器的标准和怪异模式下都能保持效果一样

[css] 不使用border画出1px高的线&#xff0c;在不同浏览器的标准和怪异模式下都能保持效果一样 <div style"width: 100%;height: 1px;"></div><hr size"1">个人简介 我是歌谣&#xff0c;欢迎和大家一起交流前后端知识。放弃很容易&am…

bzoj 3173 最长上升子序列

Written with StackEdit. Description 给定一个序列&#xff0c;初始为空。现在我们将\(1\)到\(N\)的数字插入到序列中&#xff0c;每次将一个数字插入到一个特定的位置。每插入一个数字&#xff0c;我们都想知道此时最长上升子序列长度是多少&#xff1f; Input 第一行一个整数…

java 调用linux 脚本并获取返回值

大家好&#xff0c;我是烤鸭&#xff1a; 今天分享下java 调用 shell脚本 并获取返回值。 代码实践 String cmd "df -h"; StringBuffer sb new StringBuffer(); Process process Runtime.getRuntime().exec(cmd); BufferedReader br new BufferedReader(new In…

[css] 写出主流浏览器内核私有属性的css前缀

[css] 写出主流浏览器内核私有属性的css前缀 完善一下&#xff1a; Chrome&#xff1a;Blink内核 -webkit-Safari&#xff1a;WebKit内核 -webkit-Firefox &#xff1a;Gecko内核 -moz-IE&#xff1a;Trident内核 -ms-Opera&#xff1a;Presto内核 …

补充小知识:文件句柄与文件标识符

#文件句柄 这是操作系统里的一个概念&#xff0c;句柄是WINDOWS用来标识被应用程序所建立或使用的对象的唯一整数&#xff0c;WINDOWS使用各种各样的句柄标识诸如应用程序实例&#xff0c;窗口&#xff0c;控制&#xff0c;位图&#xff0c;GDI对象等等。WINDOWS句柄有点象C语言…

[css] 使用flex实现三栏布局,两边固定,中间自适应

[css] 使用flex实现三栏布局&#xff0c;两边固定&#xff0c;中间自适应 同意里面的一个回答&#xff0c;现在有很多简单的实现方式&#xff0c;传统的这个也是一种hack的方式&#xff0c;真的没必要去追究了&#xff0c;但是核心知识点可以掌握。1.把 center 放在最前面&…

saltstack部署java应用失败无日志——CICD 部署

大家好&#xff0c;我是烤鸭&#xff1a; ​   最近在搞公司的CICD&#xff0c;遇到各种问题。复盘总结一下。 CICD 架构 这篇文章写得很详细&#xff0c;可以看一下 https://linux.cn/article-9926-1.html 而这里只是结合现在的情况分析下&#xff1a; CI 持续集成&…

day15 webUI自动化

一、webdriver的原理 driver webdriver.Chrome()创建浏览器&#xff0c;当做我们的服务端&#xff0c;代码就是客户端&#xff0c;和客户端进行ip绑定&#xff0c;基于http协议发送post请求 WebDriver webdriver是按照server – client的经典设计模式设计的。 webdriver的作用…

[css] 浏览器是怎样判断元素是否和某个CSS选择器匹配?

[css] 浏览器是怎样判断元素是否和某个CSS选择器匹配&#xff1f; 有选择器&#xff1a; div.ready #wrapper > .bg-red 先把所有元素 class 中有 bg-red 的元素拿出来组成一个集合&#xff0c;然后上一层&#xff0c;对每一个集合中的元素&#xff0c;如果元素的 parent i…

idea 插件开发 扫描sqlserver

大家好&#xff0c;我是烤鸭&#xff1a; 最近在搞sqlserver 升级 mysql/tidb&#xff0c;发现代码里的sql有很多地方需要改&#xff0c;想着能不能开发一个省点力。 官方的迁移指南&#xff1a; https://www.mysql.com/why-mysql/white-papers/sql-server-to-mysql-zh/ 方案…

VUE之文字跑马灯效果

1.效果演示 2.相关代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script src"js/vue-2.4.0.js"></script> </head> <body> <div id&…