Spring Cloud Zuul 会话保持问题
问题描述:
通过zuul智能路由到后端服务,其中有个服务是需要通过Basic Auth
认证
配置路由没问题,但请求返回始终是401,认证没通过
原因查找
查看请求参数,发现Header
信息通过Zuul
后丢失
问题分析
Zuul
默认过滤掉请求Header
,一定有对Header
过滤的配置
解决问题
#保留请求header信息
zuul.routes.xxx.sensitive-headers=Authorization,sign,sign-type
zuul.routes.xxx.custom-sensitive-headers=true
会话保持问题
通过跟踪一个HTTP请求经过Zuul
到具体服务,再到返回结果的全过程。我们很容易就能发现,在传递的过程中,HTTP请求头信息中的Cookie
和Authorization
都没有被正确地传递给具体服务,所以最终导致会话状态没有得到保持的现象。
那么这些信息是在哪里丢失的呢?我们从Zuul
进行路由转发的过滤器作为起点,来一探究竟。下面是RibbonRoutingFilter
过滤器的实现片段:
public class RibbonRoutingFilter extends ZuulFilter {
...
protected ProxyRequestHelper helper;
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
...
return null;
}
protected RibbonCommandContext buildCommandContext(RequestContext context) {
HttpServletRequest request = context.getRequest();
MultiValueMap<String, String> headers = this.helper
.buildZuulRequestHeaders(request);
MultiValueMap<String, String> params = this.helper
.buildZuulRequestQueryParams(request);
...
}
}
这里有三个重要元素:
- 过滤器的核心逻辑run函数实现,其中调用了内部函数
buildCommandContext
来构建上下文内容 - 而
buildCommandContext
中调用了helper
对象的buildZuulRequestHeaders
方法来处理请求头信息 helper
对象是ProxyRequestHelper
类的实例
接下来我们再看看ProxyRequestHelper
的实现:
public class ProxyRequestHelper {
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)) {
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()) {
headers.set(header, zuulRequestHeaders.get(header));
}
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;
}
}
...
}
}
从上述源码中,我们可以看到构建头信息的方法buildZuulRequestHeaders
通过isIncludedHeader
函数来判断当前请求的各个头信息是否在忽略的头信息清单中,如果是的话就不组织到此次转发的请求中去。那么这些需要忽略的头信息是在哪里初始化的呢?在PRE阶段的PreDecorationFilter
过滤器中,我们可以找到答案:
public class PreDecorationFilter extends ZuulFilter {
...
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("requestURI", route.getPath());
ctx.put("proxy", route.getId());
// 处理忽略头信息的部分
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper.addIgnoredHeaders(
this.properties.getSensitiveHeaders()
.toArray(new String[0]));
} else {
this.proxyRequestHelper.addIgnoredHeaders(
route.getSensitiveHeaders()
.toArray(new String[0]));
}
...
}
从上述源码中,我们可以看到有一段if/else
块,通过调用ProxyRequestHelper
的addIgnoredHeaders
方法来添加需要忽略的信息到请求上下文中,供后续ROUTE
阶段的过滤器使用。这里的if/else
块分别用来处理全局设置的敏感头信息和指定路由设置的敏感头信息。而全局的敏感头信息定义于ZuulProperties
中:
@Data
@ConfigurationProperties("zuul")
public class ZuulProperties {
private Set<String> sensitiveHeaders = new LinkedHashSet<>(
Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
...
}
所以解决该问题的思路也很简单,我们只需要通过设置sensitiveHeaders
即可,设置方法分为两种:
全局设置:
zuul.sensitive-headers=
指定路由设置:
zuul.routes.<routeName>.sensitive-headers=
zuul.routes.<routeName>.custom-sensitive-headers=true
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/04/01/spring-cloud-zuul-session-retention-issues/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论