文章目錄
? ? 1 問(wèn)題分析
? ? 2 跟蹤C(jī)ookieUtils
? ? 3 解決host地址的變化
? ? 4 網(wǎng)關(guān)的反向代理
? ? 5 Host寫(xiě)入問(wèn)題
? ? ? ? 5.1 網(wǎng)關(guān)寫(xiě)入Host
? ? ? ? 5.2 敏感頭過(guò)濾導(dǎo)致cookie沒(méi)有寫(xiě)入
? ? ? ? 5.3 防止過(guò)濾器過(guò)濾Host
? ? 6 Zuul的敏感頭過(guò)濾
接上一篇鑒權(quán)微服務(wù)中間留下的問(wèn)題,專門(mén)來(lái)分析解決一下這個(gè)問(wèn)題,首先我們登錄,然后查看cookie:
在這里插入圖片描述
卻發(fā)現(xiàn)cookie中空空如也,這是為什么?
1 問(wèn)題分析
我們?cè)谥皽y(cè)試時(shí),清晰的看到了響應(yīng)頭中,有Set-Cookie屬性
在這里插入圖片描述
為什么在這里卻什么都沒(méi)有?
我們之前在講cors跨域時(shí),講到過(guò)跨域請(qǐng)求cookie生效的條件:
? ? 服務(wù)的響應(yīng)頭中需要攜帶Access-Control-Allow-Credentials并且為true。
? ? 響應(yīng)頭中的Access-Control-Allow-Origin一定不能為*,必須是指定的域名
? ? 瀏覽器發(fā)起ajax需要指定withCredentials 為true
看看我們的服務(wù)端cors配置:
在這里插入圖片描述
沒(méi)有任何問(wèn)題。
我們?cè)賮?lái)仔細(xì)看一下區(qū)別:調(diào)試的時(shí)候訪問(wèn)的是http://localhost:8087/login,但是瀏覽器訪問(wèn)的卻是http://api.leyou.com/api/auth/ogin,那我們?cè)儆眠@個(gè)地址調(diào)試一次:
在這里插入圖片描述
現(xiàn)在我們找到問(wèn)題了!——路徑問(wèn)題!
但是路徑變化會(huì)引起什么變化呢?——網(wǎng)關(guān)問(wèn)題或者nginx問(wèn)題二者都有可能產(chǎn)生問(wèn)題,要逐一排查
2 跟蹤C(jī)ookieUtils
除了上面那兩個(gè)原因,其次我們想,寫(xiě)cookie這個(gè)代碼有沒(méi)有問(wèn)題,為什么剛才有現(xiàn)在又沒(méi)有了呢?我們寫(xiě)cookie使用的是工具類:CookieUtils,先看一下可以正常生成的cookie信息:
返回header:
? ? LY_TOKEN=eyJhbGciOiJSUzI1NiJ9.eyJpZCI6MzIsInVzZXJuYW1lIjoiZGlhbmVtYXgiLCJleHAiOjE1NTUzMzE2MjF9.cYjoy_DaqlYx5GumxU7TExtENS1KBvNg_Sjdo1PBcW_tjYBu1xXtWfkwQV1_y03ttDcvs0PF3fQWkJOkmICv3n8Dy0do_M6KMMjG7fcNW-Mmk2blunOhw69o9ZSx0W0MSNGVMjR38OLyi9OumG3FzX2XjRB6GO_veBwMB5cmU; Domain=localhost; Path=/;HttpOnly
我們發(fā)現(xiàn)cookie的 domain屬性似乎不太對(duì)。我們?nèi)ebug跟蹤C(jī)ookieUtils,看看到底是怎么回事:
? ? 我們發(fā)現(xiàn)內(nèi)部有一個(gè)方法,用來(lái)獲取Domain,先通過(guò)request.getRequestURL()方法來(lái)獲取請(qǐng)求路徑
? ? 我們?cè)O(shè)置斷點(diǎn),在產(chǎn)生token的 cookieUtils中的build()函數(shù)設(shè)置斷點(diǎn),單步執(zhí)行
? ? 執(zhí)行到setDomain,首先由request.getRequestURL()獲取請(qǐng)求路徑
? ? 此時(shí)發(fā)現(xiàn)請(qǐng)求路徑被改變了,發(fā)現(xiàn)URL從http://www.leyou.com/變成了serverNamehttp://127.0.0.1:8087/login
? ? 之后經(jīng)過(guò)一些截取操作,serverName變成了0.0.1,
? ? 但如果是api.leyou.com的話,最終就變成了leyou.com,而leyou.com是所有相關(guān)網(wǎng)站的共同后綴,可以供leyou所有網(wǎng)站來(lái)訪問(wèn),這就很完美
在這里插入圖片描述
? ? 那原因是什么呢?——serverName的問(wèn)題
? ? serverName是http://127.0.0.1:8087/login,所以不管怎么截取都是有問(wèn)題的
? ? 問(wèn)題找到了:
? ? 我們請(qǐng)求時(shí)的serverName明明是:api.leyou.com,現(xiàn)在卻被變成了:127.0.0.1,因此計(jì)算domain是錯(cuò)誤的,cookie也是有domain(域) 的限制,一個(gè)網(wǎng)頁(yè),只能操作當(dāng)前域名下的cookie,但是現(xiàn)在我們看到的地址是0.0.1,而頁(yè)面是www.leyou.com,域名不匹配,cookie設(shè)置肯定失敗了!
3 解決host地址的變化
那么問(wèn)題來(lái)了:為什么我們這里的請(qǐng)求serverName變成了:127.0.0.1:8087呢?
這里的serverName其實(shí)就是請(qǐng)求的時(shí)的主機(jī)名:Host,serverName沒(méi)問(wèn)題,后續(xù)的自然會(huì)沒(méi)問(wèn)題,之所以改變,原因就是反向代理:當(dāng)訪問(wèn)leyou域名時(shí),這個(gè)域名指向了虛擬機(jī)
我們打開(kāi)nginx.conf,查看配置:
在這里插入圖片描述
要想解決這件事,首先要知道request.getRequestURL tomcat是怎么拿到域名地址的?
首先在瀏覽器F12控制臺(tái) 隨便打開(kāi)一個(gè)js請(qǐng)求
在這里插入圖片描述可以發(fā)現(xiàn)Request URL:http://www.leyou.com/plugins/jquery/jquery.min.js
上述URL在request中其實(shí)被分成了幾段來(lái)表示:
? ? Host:www.leyou.com——host
? ? GET /js/plugins/jquery/jquery.min.js HTTP/1.1——路徑和協(xié)議
全路徑其實(shí)就是請(qǐng)求 host+端口(默認(rèn)是80)+路徑 拼在一起得到的 ,影響我們得到域名的原因就是Host ,后面的路徑我們不關(guān)心! host其實(shí)是請(qǐng)求頭的一部分,當(dāng)我們反向代理的時(shí)候,nginx已經(jīng)將請(qǐng)求頭Host轉(zhuǎn)換成了192.168.124.1,于是在nginx反向代理時(shí)多設(shè)置一個(gè)Host頭。
將nginx的conf文件修改,添加:
proxy_set_header Host $host
? ? 1
$host代表的是上一次原生請(qǐng)求的host,原生的請(qǐng)求一定是api.leyou.com,它會(huì)讀取原生請(qǐng)求的host放到$host的位置,變成
proxy_set_header Host api.leyou.com
? ? 1
到此為止,重啟nginx
nginx -s reload
? ? 1
再次測(cè)試:
在這里插入圖片描述
還是有問(wèn)題。。。
4 網(wǎng)關(guān)的反向代理
其實(shí)剛才的問(wèn)題沒(méi)有找全,nginx把請(qǐng)求路徑代理到了htto://192.168.124.1:10010,但是我們拿到的serverName還是http://127.0.0.1:8087,怎么從10010端口變成了8087端口呢,因此這不光是nginx的問(wèn)題,網(wǎng)關(guān)也做了一次反向代理
因?yàn)榫W(wǎng)關(guān)是不能自己處理的,他會(huì)把請(qǐng)求轉(zhuǎn)發(fā)到微服務(wù)8087來(lái)進(jìn)行處理,現(xiàn)在我們把網(wǎng)關(guān)用debug啟動(dòng),在網(wǎng)關(guān)中有很多過(guò)濾器,這些過(guò)濾器默認(rèn)繼承自ZuulFilter,在其中一個(gè)PreDecorationFilter的run方法打個(gè)斷點(diǎn),查詢host,調(diào)用方法ctx.getRequest().getHeader("host"):
在這里插入圖片描述
發(fā)現(xiàn)此時(shí)的host是api.leyou.com 是正確的,說(shuō)明我們nginx的配置生效了,理論上此時(shí)放行,是不會(huì)出現(xiàn)錯(cuò)誤的,如果此時(shí)出現(xiàn)錯(cuò)誤,那就是網(wǎng)關(guān)沒(méi)有將host寫(xiě)進(jìn)去
5 Host寫(xiě)入問(wèn)題
5.1 網(wǎng)關(guān)寫(xiě)入Host
事實(shí)上,到此為止并沒(méi)有將host寫(xiě)進(jìn)去,原因是有個(gè)if判斷 ——porperties.isAddHostHeader(),成立才將host寫(xiě)入,點(diǎn)進(jìn)方法發(fā)現(xiàn)AddHostHeader是一個(gè)boolean值,值為false,屬于ZuulProperties,前綴是zuul,修改這個(gè)值很簡(jiǎn)單:
在這里插入圖片描述
繼續(xù)debug運(yùn)行看看有沒(méi)有寫(xiě)入成功:
在這里插入圖片描述
可以看到domain寫(xiě)入成功了,那我們來(lái)測(cè)試一下:
在這里插入圖片描述
發(fā)現(xiàn),響應(yīng)頭中還是沒(méi)有set-cookie!事實(shí)上到這里還沒(méi)有結(jié)束,單單設(shè)置這個(gè)是不行的
5.2 敏感頭過(guò)濾導(dǎo)致cookie沒(méi)有寫(xiě)入
過(guò)濾器中還有一個(gè)addIgnoredHeaders方法,會(huì)對(duì)一些頭進(jìn)行忽略,會(huì)對(duì)敏感頭進(jìn)行過(guò)濾
在這里插入圖片描述
會(huì)發(fā)現(xiàn),這里會(huì)通過(guò)一個(gè)屬性為SensitiveHeaders的屬性,來(lái)獲取敏感頭列表,然后添加到IgnoredHeaders中,這些頭信息就會(huì)被忽略。
而這個(gè)SensitiveHeaders的默認(rèn)值就包含了set-cookie:
在這里插入圖片描述
現(xiàn)在set-cookie被濾掉,因此問(wèn)題和原因我們知道了,但是先不管,我們先來(lái)看Host可不可以正常傳遞,debug發(fā)現(xiàn)Host還是沒(méi)有被拿到?。?!
5.3 防止過(guò)濾器過(guò)濾Host
ZuulFilter下面還有一個(gè)叫RibbonRoutingFilter的過(guò)濾器,做負(fù)載均衡路由的方法
在這里插入圖片描述
它有一個(gè)構(gòu)建上下文的方法:
在這里插入圖片描述
在構(gòu)建上下文中會(huì)先獲取頭信息:
在這里插入圖片描述
然后對(duì)頭做一些判斷:
在這里插入圖片描述
判斷方法:
在這里插入圖片描述
如果是被允許的頭信息,才會(huì)將其添加到Headers中去,如果頭是被忽略的,則不會(huì)被添加,但是下面有一個(gè)switch語(yǔ)句,如果name是host,則忽略!因此host永遠(yuǎn)不會(huì)被添加進(jìn)去
最后:盡管yml中設(shè)置了true,Host被添加進(jìn)去了,但是由于這個(gè)過(guò)濾器,又被忽略掉了。因此無(wú)論如何我們都沒(méi)辦法把Host添加進(jìn)去,這是一個(gè)bug
解決辦法:
對(duì)比了上一個(gè)SpringCloud版本的源碼,發(fā)現(xiàn)是沒(méi)有if判斷的,說(shuō)明是因?yàn)榧恿诉@一段導(dǎo)致錯(cuò)誤,因此我們修改一下版本
在這里插入圖片描述
再次測(cè)試:
在這里插入圖片描述
Host終于被添加進(jìn)來(lái)了!此時(shí)再次debug程序,可以看到已經(jīng)拿到了正確的domain:
在這里插入圖片描述
6 Zuul的敏感頭過(guò)濾
我們已經(jīng)在前面5.2部分分析了cookie沒(méi)有被寫(xiě)入的原因,我們現(xiàn)在來(lái)解決它:
解決方案有兩種:
全局設(shè)置:
? ? zuul.sensitive-headers=
? ? 在這里插入圖片描述
指定路由設(shè)置:
? ? zuul.routes.<routeName>.sensitive-headers=
? ? zuul.routes.<routeName>.custom-sensitive-headers=true
思路都是把敏感頭設(shè)置為null
現(xiàn)在再來(lái)測(cè)試一次:
在這里插入圖片描述
一切正常,到此位置,cookie的寫(xiě)入問(wèn)題被完美解決了