Springcloud的版本是Greenwich.SR2,Springboot版本是2.1.6.release.java
在使用zuul時,我有倆個需求,一是不讓zuul過濾頭部的Cookie,二是要在zuul網關對request的header設置requestId——便於鏈路追蹤。git
在網上搜了下,發現sensitiveHeaders和ignoredHeaders描述的較多,可是大多描述的不是較詳細,同時本身也想知道底層上是如何作的,因此看了下源碼,記錄下。以下List-1,在application.yml中設置zuul的sensitiveHeaders和ignoredHeaders,先來講結論,sensitiveHeaders的值設置爲x1後servletRequest header的x1不會傳到下游;ignoredHeaders的值設置爲x2後,servletRequest header的x2不會傳到下游。github
List-1spring
zuul: sensitiveHeaders: x1 ignoredHeaders: x2
以下List-2所示,List-1中的配置會被Spring解析到ZuulProperties,sensitiveHeaders是有默認值的,即Cookie、Set-Cookie、Authorization。bash
List-2app
@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,ide
List-3ui
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-4this
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 } }
來看RibbonRoutingFilter的run方法,以下List-5,run()調用buildCommandContext來構造RibbonCommand,buildCommandContext方法中調用了ProxyRequestHelper的buildZuulRequestHeaders方法,如List-6所示。url
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; } }
注:鏈路爲何從PreDecorationFilter到RibbonRoutingFilter的,這和Zuul的內部的ZuulFilter機制有關。
要注意的是,若是要往下游傳的header含有大寫的,那麼下游接收到的header是小寫的,緣由在List-4中能夠看出。