新聞中心
由于我們在之前所有的入門教程中,對于HTTP請求都采用了簡單的接口實現(xiàn)。而實際使用過程中,我們的HTTP請求要復(fù)雜的多,比如當(dāng)我們將Spring Cloud Zuul作為API網(wǎng)關(guān)接入網(wǎng)站類應(yīng)用時,往往都會碰到下面這兩個非常常見的問題:

公司主營業(yè)務(wù):成都做網(wǎng)站、網(wǎng)站制作、移動網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)推出新民免費做網(wǎng)站回饋大家。
- 會話無法保持
- 重定向后的HOST錯誤
本文將幫助大家分析問題原因并給出解決這兩個常見問題的方法。
一、會話保持問題
通過跟蹤一個HTTP請求經(jīng)過Zuul到具體服務(wù),再到返回結(jié)果的全過程。我們很容易就能發(fā)現(xiàn),在傳遞的過程中,HTTP請求頭信息中的Cookie和Authorization都沒有被正確地傳遞給具體服務(wù),所以最終導(dǎo)致會話狀態(tài)沒有得到保持的現(xiàn)象。
那么這些信息是在哪里丟失的呢?我們從Zuul進行路由轉(zhuǎn)發(fā)的過濾器作為起點,來一探究竟。下面是RibbonRoutingFilter過濾器的實現(xiàn)片段:
- 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
headers = this.helper - .buildZuulRequestHeaders(request);
- MultiValueMap
params = this.helper - .buildZuulRequestQueryParams(request);
- ...
- }
- }
這里有三個重要元素:
- 過濾器的核心邏輯run函數(shù)實現(xiàn),其中調(diào)用了內(nèi)部函數(shù)buildCommandContext來構(gòu)建上下文內(nèi)容
- 而buildCommandContext中調(diào)用了helper對象的buildZuulRequestHeaders方法來處理請求頭信息
- helper對象是ProxyRequestHelper類的實例
接下來我們再看看ProxyRequestHelper的實現(xiàn):
- public class ProxyRequestHelper {
- public MultiValueMap
buildZuulRequestHeaders( - HttpServletRequest request) {
- RequestContext context = RequestContext.getCurrentContext();
- MultiValueMap
headers = new HttpHeaders(); - Enumeration
headerNames = request.getHeaderNames(); - if (headerNames != null) {
- while (headerNames.hasMoreElements()) {
- String name = headerNames.nextElement();
- if (isIncludedHeader(name)) {
- Enumeration
values = request.getHeaders(name); - while (values.hasMoreElements()) {
- String value = values.nextElement();
- headers.add(name, value);
- }
- }
- }
- }
- Map
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;
- }
- }
- ...
- }
- }
從上述源碼中,我們可以看到構(gòu)建頭信息的方法buildZuulRequestHeaders通過isIncludedHeader函數(shù)來判斷當(dāng)前請求的各個頭信息是否在忽略的頭信息清單中,如果是的話就不組織到此次轉(zhuǎn)發(fā)的請求中去。那么這些需要忽略的頭信息是在哪里初始化的呢?在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塊,通過調(diào)用ProxyRequestHelper的addIgnoredHeaders方法來添加需要忽略的信息到請求上下文中,供后續(xù)ROUTE階段的過濾器使用。這里的if/else塊分別用來處理全局設(shè)置的敏感頭信息和指定路由設(shè)置的敏感頭信息。而全局的敏感頭信息定義于ZuulProperties中:
- @Data
- @ConfigurationProperties("zuul")
- public class ZuulProperties{
- private Set
sensitiveHeaders = new LinkedHashSet<>( - Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
- ...
- }
所以解決該問題的思路也很簡單,我們只需要通過設(shè)置sensitiveHeaders即可,設(shè)置方法分為兩種:
1. 全局設(shè)置:
- zuul.sensitive-headers=
2. 指定路由設(shè)置:
- zuul.routes. .sensitive-headers=
- zuul.routes. .custom-sensitive-headers=true
二、重定向問題
在使用Spring Cloud Zuul對接Web網(wǎng)站的時候,處理完了會話控制問題之后。往往我們還會碰到如下圖所示的問題,我們在瀏覽器中通過Zuul發(fā)起了登錄請求,該請求會被路由到某WebSite服務(wù),該服務(wù)在完成了登錄處理之后,會進行重定向到某個主頁或歡迎頁面。此時,仔細的開發(fā)者會發(fā)現(xiàn),在登錄完成之后,我們?yōu)g覽器中URL的HOST部分發(fā)生的改變,該地址變成了具體WebSite服務(wù)的地址了。這就是在這一節(jié),我們將分析和解決的重定向問題!
出現(xiàn)該問題的根源是Spring Cloud Zuul沒有正確的處理HTTP請求頭信息中的Host導(dǎo)致。在Brixton版本中,Spring Cloud Zuul的PreDecorationFilter過濾器實現(xiàn)時完全沒有考慮這一問題,它更多的定位于REST API的網(wǎng)關(guān)。所以如果要在Brixton版本中增加這一特性就相對較為復(fù)雜,不過好在Camden版本之后,Spring Cloud Netflix 1.2.x版本的Zuul增強了該功能,我們只需要通過配置屬性zuul.add-host-header=true就能讓原本有問題的重定向操作得到正確的處理。關(guān)于更多Host頭信息的處理,讀者可以參考本文之前的分析思路,可以通過查看PreDecorationFilter過濾器的源碼來詳細更多實現(xiàn)細節(jié)。
【本文為專欄作者“翟永超”的原創(chuàng)稿件,轉(zhuǎn)載請通過聯(lián)系作者獲取授權(quán)】
網(wǎng)站名稱:SpringCloud實戰(zhàn)小貼士:Zuul處理Cookie和重定向
本文鏈接:http://m.5511xx.com/article/coicscd.html


咨詢
建站咨詢
