Springcloud之zuul的过滤头部
Springcloud的版本是Greenwich.SR2,Springboot版本是2.1.6.release.
在使用zuul时,我有俩个需求,一是不让zuul过滤头部的Cookie,二是要在zuul网关对request的header设置requestId——便于链路追踪。
在网上搜了下,发现sensitiveHeaders和ignoredHeaders描述的较多,但是大多描述的不是较详细,同时自己也想知道底层上是如何做的,所以看了下源码,记录下。如下List-1,在application.yml中设置zuul的sensitiveHeaders和ignoredHeaders,先来说结论,sensitiveHeaders的值设置为x1后servletRequest header的x1不会传到下游;ignoredHeaders的值设置为x2后,servletRequest header的x2不会传到下游。
List-1
zuul:
sensitiveHeaders: x1
ignoredHeaders: x2
如下List-2所示,List-1中的配置会被Spring解析到ZuulProperties,sensitiveHeaders是有默认值的,即Cookie、Set-Cookie、Authorization。
List-2
@ConfigurationProperties("zuul")
public class ZuulProperties {
...
private Set<String> ignoredHeaders = new LinkedHashSet<>();
...
private Set<String> sensitiveHeaders = new LinkedHashSet<>(
Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
...
来看PreDecorationFilter的run方法实现,如下List-3,1处和2处,会将ZuulProperties中sensitiveHeaders的值加入到要过滤的字段里面,来看下ProxyRequestHelper的addIgnoredHeaders方法,如下List-4所示,RequestContext是个ConcurrentHashMap,
List-3
public class PreDecorationFilter extends ZuulFilter {
...
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
final String requestURI = this.urlPathHelper
.getPathWithinApplication(ctx.getRequest());
Route route = this.routeLocator.getMatchingRoute(requestURI);
if (route != null) {
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper.addIgnoredHeaders(//1
this.properties.getSensitiveHeaders().toArray(new String[0]));
}
else {
this.proxyRequestHelper.addIgnoredHeaders(//2
route.getSensitiveHeaders().toArray(new String[0]));
}
...
}
List-4
public void addIgnoredHeaders(String... names) {
RequestContext ctx = RequestContext.getCurrentContext();
if (!ctx.containsKey(IGNORED_HEADERS)) {
ctx.set(IGNORED_HEADERS, new HashSet<String>());
}
@SuppressWarnings("unchecked")
Set<String> set = (Set<String>) ctx.get(IGNORED_HEADERS);
for (String name : this.ignoredHeaders) {
set.add(name.toLowerCase());//1
}
for (String name : names) {
set.add(name.toLowerCase());//2
}
}
- List-4中,1处this.ignoredHeaders()会获取ZuulProperties中的ignoredHeaders,之后加入到HashSet中。
- 2处,将方法上的参数值全部加入到HashSet中。
- 这样,我们设置的sensitiveHeaders和ignoredHeaders全部加到HashSet中,需要注意的是1处和2处都调用了toLowerCase()方法,所以下游收到的header中的字段key都是小写的。
来看RibbonRoutingFilter的run方法,如下List-5,run()调用buildCommandContext来构造RibbonCommand,buildCommandContext方法中调用了ProxyRequestHelper的buildZuulRequestHeaders方法,如List-6所示。
List-5
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
...
}
protected RibbonCommandContext buildCommandContext(RequestContext context) {
HttpServletRequest request = context.getRequest();
MultiValueMap<String, String> headers = this.helper
.buildZuulRequestHeaders(request);
...
}
List-6
public MultiValueMap<String, String> buildZuulRequestHeaders(
HttpServletRequest request) {
RequestContext context = RequestContext.getCurrentContext();
MultiValueMap<String, String> headers = new HttpHeaders();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
if (isIncludedHeader(name)) {//1
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
String value = values.nextElement();
headers.add(name, value);
}
}
}
}
Map<String, String> zuulRequestHeaders = context.getZuulRequestHeaders();
for (String header : zuulRequestHeaders.keySet()) {
if (isIncludedHeader(header)) {//2
headers.set(header, zuulRequestHeaders.get(header));
}
}
if (!headers.containsKey(HttpHeaders.ACCEPT_ENCODING)) {
headers.set(HttpHeaders.ACCEPT_ENCODING, "gzip");
}
return headers;
}
public boolean isIncludedHeader(String headerName) {
String name = headerName.toLowerCase();
RequestContext ctx = RequestContext.getCurrentContext();
if (ctx.containsKey(IGNORED_HEADERS)) {
Object object = ctx.get(IGNORED_HEADERS);
if (object instanceof Collection && ((Collection<?>) object).contains(name)) {
return false;
}
}
switch (name) {
case "host":
if (addHostHeader) {
return true;
}
case "connection":
case "content-length":
case "server":
case "transfer-encoding":
case "x-application-context":
return false;
default:
return true;
}
}
- 获取HttpServletRequest的header,之后遍历,调用isIncludedHeader方法,isIncludedHeader里面获取RequestContext,判断当前的这个header key是不是在IGNORED_HEADERS这个集合里面——List-4中设置的,如果在里面,那么返回false,不会将这个header字段往下游传。
- 2处,context.getZuulRequestHeaders()获取我们手动设置的header(调用addZuulResponseHeader方法设置),之后逐个遍历,如果在IGNORED_HEADERS这个集合里面——List-4中,则不会加入到headers中,即不往下游传。
注:链路为什么从PreDecorationFilter到RibbonRoutingFilter的,这和Zuul的内部的ZuulFilter机制有关。
要注意的是,如果要往下游传的header含有大写的,那么下游接收到的header是小写的,原因在List-4中可以看出。
Reference
- python利用pysvn发布lib的小程序
- 《笨办法学Python》 第6课手记
- React Native之ViewPagerAndroid 组件
- 《笨办法学Python》 第5课手记
- psRobot:植物小RNA分析系统
- 《笨办法学Python》 第3课手记
- NGS基础 - 参考基因组和基因注释文件
- 关于Android PullTorefreshScrollview回到顶部实例
- 《笨办法学Python》 第2课手记
- 《笨办法学Python》 第1课手记
- 《笨办法学Python》 第40课手记
- 很经典的GDB调试命令,包括查看变量,查看内存
- 《笨办法学Python》 第39课手记
- 《笨办法学Python》 第38课手记
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- linux环境搭建图数据库neo4j的讲解
- Linux编程之ICMP洪水攻击
- linux搭建squid代理服务器的完整步骤
- 使用wget递归镜像网站
- 整理Linux中字符串的相关操作技巧
- Ubuntu 16.04下无法安装.deb的解决方法
- 关于linux中系统输入输出的管理详解
- Linux下IP设置脚本的实例及遇到问题解决办法
- Linux与Windows文件互传(VMWare)
- 如何测试Linux下tcp最大连接数限制详解
- Linux中利用Vim对文件进行密码保护的方法详解
- Linux中禁止用户修改/重置密码
- 详解 MAC/Linux Vi配置环境变量及Java环境变量配置
- 扒开DMA映射的内裤
- 如何理解与识别Linux中的文件类型