一個“403”問題的產生及解決
最近在測試一個專案的時候,遇到了一個比較詭異的“403”問題問題。在經過不斷的查詢資料和諮詢大師級的人物之後,問題終於有了解決方案。現在就把我在整個過程中遇到的坑記錄下來,可以讓大家後續遇到類似問題有所參考。
解決過程:
1. 剛看到這個問題,第一個想法是這樣:
由於nginx做了負載均衡,那麼一個指令碼中的3個請求:request1,request2,request3分別給均衡到了伺服器A,伺服器B,伺服器C上面。所以對於使用者perftest1來說,他的登入請求被分配到了伺服器A上,檢視題目的請求被分配到了伺服器B上,提交答案的請求被分配到了伺服器C上。被分配到伺服器C上的請求request3,仍然在苦苦等著屬於他自己的patsid,但是他自己的patsid卻在伺服器A上,他永遠也拿不到,所以產生了 403請求,被伺服器以不正確的patsid為由拒絕了perftest1的提交答案的請求。
請教了一些同事後,他們給的建議是採用nginx的sticky模組。在多臺後臺伺服器的環境下,我們為了確保一個客戶只和一臺伺服器通訊,我們勢必使用長連線。使用什麼方式來實現這種連線呢,常見的有使用nginx自帶的ip_hash來做,我想這絕對不是一個好的辦法,如果前端是CDN,或者說一個區域網的客戶同時訪問伺服器,導致出現伺服器分配不均衡,以及不能保證每次訪問都粘滯在同一臺伺服器。如果基於cookie會是一種什麼情形,想想看,每臺電腦都會有不同的cookie,在保持長連線的同時還保證了伺服器的壓力均衡,nginx sticky值得推薦。如果瀏覽器不支援cookie,那麼sticky不生效,畢竟整個模組是給予cookie實現的。nginx sticky 模組工作流程圖如下:
但是,在nginx的配置檔案中新增了sticky模組之後,問題並沒有解決。方案一以失敗告終。
2. 採用tcpdump抓包檢視
在伺服器端添加了日誌,然後用“伺服器A_IP :port”作為請求URL的時候,日誌中的Parameters如下所示:
當用“server_name” 作為請求URL的時候,日誌中的Parameters如下所示:
[["f78494dc412a0455a5b67f68707b2b97",null]]
很明顯,用“server_name” 作為請求URL的請求引數中cookie和session都是空的,這也難怪會被403,給拒絕掉。
但是我的測試指令碼中明明是有Set-Cookie的。為了一探究竟,用tcpdump進行抓包:
可以發現cookie中的引數是我在指令碼中set進去的,登入獲得的patsid。但是卻被伺服器拒絕掉了。
3. 終於查到根源
諮詢了nginx大牛——劉成同學,終於找到了nginx的403問題的根源了,問題就在出現在請求引數的header中的這個引數'authenticity_token'。
因為nginx中的自身很多變數都是以下劃線標識的,所以遇到這個'authenticity_token'的時候,nginx為了防止混淆,他會把使用者發過來的過濾掉,所以就識別不到這個token值。解決方法是在nginx的配置的http模組中新增 underscores_in_headers on;(這個預設值是關閉的) 然後重啟nginx之後,就可以用指令碼返回正常的請求結果了。
HTTP頭部是否允許下劃線
語法:underscores_in_headers on | off;
預設:underscores_in_headers off;
配置塊:http、server
預設為off,表示HTTP頭部的名稱中不允許帶“_”(下劃線)。
可以檢視ngx_http_core_module 模組的說明。
至此,一個由nginx引發的403問題終於解決掉了,希望能給大家以幫助。