摘要:這種情況通常發生在反向代理的時候,前端發起請求代理服務器,代理服務器發起請求到,這時候就容易導致域名不一致,請一定要注意這點。
寫在最前 前后端分離其實有兩類:
開發階段使用dev-server,生產階段是打包成靜態文件整個放入后端項目中。
開發階段使用dev-server,生產階段是打包成靜態文件放入多帶帶的靜態資源服務器中,如nginx。
這兩種方案最大的區別就是生產階段。由于第一種方案前端和后端本質在同一個服務中的,所以壓根就沒有跨域,配置cas的坑比較少。而第二種方案我們一般使用nginx反向代理完成跨域,配置cas的坑會很多。為了后面分析方便,我們分別稱上述兩種方案為『前后端分離A』和『前后端分離B』
請求也分為兩類:1.HTTP請求:像瀏覽器地址欄發起的請求、瀏覽器自發的訪問某個網址、Postman測試接口,這些行為其實都是發起的HTTP請求,不會有跨域問題。
2.AJAX(XMLHttpRequest)請求:這是瀏覽器內部的XMLHttpRequest對象發起的請求,瀏覽器會禁止其發起跨域的請求,主要是為了防止跨站腳本偽造的攻擊(CSRF)。
難點分析前后端分離、跨域、CAS這三項技術多帶帶使用起來,甚至拿其中兩個出來一起使用,難度都不大,下面來列舉一下:
前后端分離(AB)+跨域
前后端分離A+CAS(因為A方案根本就沒有跨域這一說)
前后端分離B+跨域+CAS
前后端分離(AB)+跨域這個最簡單,只有跨域,沒有CAS,常見的CORS、反向代理、JSONP都可以解決
前后端分離A+CAS 坑:CAS認證過期,莫名出現跨域錯誤的問題可能有人會問,剛才不是說方案A壓根就沒有跨域問題嗎?其實,這個跨域錯誤不怪我們的后端,而是怪CAS那邊的后端,待我詳細說來。
正常情況下,CAS認證成功后,瀏覽器會設置好一個來自CAS的Cookie以維持與CAS的Session。之后每次請求,無論是ajax請求還是http請求,都會帶上這個cookie。而我們自己的后端服務器也會有一個CAS Authorization的過濾器,把沒有CAS認證過的請求重定向到CAS的login頁面。因為本次請求我們帶了cas的cookie,所以請求順利通過filter來到controller層,進而返回數據。
但是考慮這樣一個情況,今天你打開你的瀏覽器,訪問一個你們新做的cms系統的網址,然后跳到cas login頁面,正常登陸,正常使用。然后來到第二天早上,因為昨天的頁面你沒關,你直接點了一個查詢按鈕,結果報錯了。你打開瀏覽器控制臺,竟然發現報了一個跨域的錯誤。這里有兩處困惑:
為什么他喵的會跨域呢?我們前后端命名部署在一臺服務器上,是同域的啊。
為什么cas認證失效后,沒有自動跳到cas登錄頁呢?可是我之前直接在瀏覽器輸入cms系統的地址時,因為沒認證過,瀏覽器是能直接跳到cas登錄頁的,為什么這次不行呢?
ajax到底怎么處理302的
為什么會跨域:
想想一下這樣的一個流程:第二天早上你來,點擊一個查詢按鈕,發起了ajax請求,請求中帶上了一個已經失效的cookie,然后請求被后端cas filter攔截,發現已失效,讓后302跳轉到cas login界面。在這個過程中,你之前發起的ajax請求其實被redirect到了cas的login.html頁面(這只是表象,本質后面會提到)。你相當于發起ajax請求去請求一個html文件下來,然而cas的服務器并沒有配置跨域,為了安全考慮也不能配置跨域,所以你的ajax請求還沒來得及請求下來數據,你就被瀏覽器認為是跨域了,因為你的確在請求cas服務器的一個靜態資源。
退一萬步說,就算cas服務器配置了跨域,雖然你點擊查詢按鈕的行為不會報跨域錯誤了,但你依然不能自動跳轉到cas login頁面,因為這個login.html直接當做你ajax的success中的回調參數回來了,瀏覽器是不會幫你跳轉的。
為什么不能跳轉:
首先,你打開瀏覽器輸入cms系統的地址去訪問的時候,發起的是HTTP請求,是不存在跨域問題的。因此你的HTTP請求被后端的filter給redirect到了cas的login.html,這個流程是沒問題的。而你點擊查詢按鈕,發起的是ajax請求,是沒法跳轉的(具體原因見下方文字)
ajax在302中的行為本質
當你點擊查詢按鈕,發起的是ajax請求,請求被后端filter攔截,并告知你302跳轉到login頁,此時瀏覽器首先會感知到這次ajax請求的302狀態,并替ajax去訪問要跳轉到的地址,然后將訪問的結果(其實就是整個login.html頁面)返回到你的ajax的success回調函數中,因此這個回調函數的參數其實就是整個login.html的頁面。并且,直到瀏覽器把html放到ajax的success回調函數后,ajax才會真正的回調,之前的302狀態ajax是感知不到的,當然也獲取不到,所以想通過ajax判斷status是否是302,進而手動location.href到login頁的方案是不行的。
其實,這么看起來就像是你的ajax直接請求到了login.html頁面。
另外,在實際cas跳轉的過程中,在ajax的success回調之前,你的ajax操作就被瀏覽器認為是跨域了,所以你壓根就沒機會回調success,也因此獲取不到status狀態或者那個沒卵用的login.html。
我們要實現的就是:在cookie失效時,點擊查詢按鈕后,能自動跳轉到cas登錄頁。
方案很多,但都靠一下兩點:
用HTTP請求替代Ajax請求去跳轉到登錄頁
用200代替302告知ajax當前請求的狀態
舉幾個例子:
1、錯誤方案:設法攔截ajax的response,然后判斷response的status是否是302,如果是302就手動location.href跳到cas登錄頁,但是這樣是不行的,因為我們根本獲取不到這個302狀態。
2、必須要后端配合,后端需要額外加1個filter和1個controller, 起個名字吧,就叫ValidateFilter和ValidateController吧。
ValidateFilter只過濾那些需要被cas攔截的請求,在doFilter里面判斷HttpServletRequest的狀態,看看這個request里能不能獲取到當前用戶名,如果能獲取到,代表認證沒問題,讓這個請求繼續往下走chain.doFilter,如果不能獲取到,代表認證失效了(因為filter不能直接返回,所以我們需要一個ValidateController),我們request.dispatch這個請求到ValidateController的redirect方法中(自己寫的),讓這個redirect方法返回一個result,result中設置一個標志,比如給code:xxx。
然后前端設法在ajax的response之前獲取response的result,看看result的code是否為xxx,如果是,那就location.href跳轉到cas登錄頁即可,其中service參數寫cas登陸之后要回調的后端接口,然后讓后端去跳轉到前端頁面。
為什么不能直接service寫前端?
因為我們不僅要跟cas服務器維持session,還要跟我們自己的后端維持session,如果不回調后端,后端就不會感知到我們的登錄狀態了。
比如:
//前端: if(result.code === xxx) { location. //currentPath是為了login之后再調回當前頁面 } //后端 filter 偽代碼: void doFilter(request, response, chain) { if(request中有用戶名) { chain.doFilter() } else if(request.uri == "/redirect/to/caslogin") { chain.doFilter() } else { request.dispatch("/redirect/to/caslogin") } } //后端 controller 偽代碼 // 用來接受filter過來的那些認證失效的請求 @path("/redirect/to/caslogin") String redirectToCasLogin(request, response) { return { "code": xxx } } // 用來在login之后回調用 @path("/redirect/to/frontend") String redirectToFrontend(request, response) { String path = request中的currentPath參數 request.sendRedirect(path) } // 另外,這個controller一定不要被validateFilter過濾,因為如果這個controller也要被過濾,那就陷入cas驗證的死循環了。
3.和2類似,但是location.href中直接寫
location.href = "http://后端服務器地址/redirect/to/caslogin?currentPath=當前頁面路徑"
此時我們直接請求后端接口/redirect/to/caslogin,他首先被validateFilter攔截,但是因為有一個if判斷,他被直接doFilter,然后請求來到了cas的Filter,因為沒登錄,該filter會自動拼接我們配置的cas serverName+當前請求的uri,同樣會形成
"http://cas.server.com/login?service=http://后端服務器地址/redirec...徑"這樣的url。
寫不動了,總之要注意:要保持cookie的域一致
對于nginx,如果從 www.a.com/ 代理到 www.b.com/api,那么形成的cookie的域是會是/api,而瀏覽器發起請求時只能攜帶/域的cookie,所以導致cookie丟失,session失效。可以通過nginx配置,把/api域下的cookie都放到/即可解決。
為了避免額外的麻煩,最好保持代理前后url一致吧,即都有一個/api前綴,或者都沒有。
對于瀏覽器,發起的ajax所帶的cookie是發起請求的host域名有嚴格關系的,不同的域名帶不同的cookie,所以如果出現,你明明已經登陸了,但是在此發起ajax請求,后端還是識別不出來你的登錄狀態,那就可能是你發起的請求的域名不一致了。也就是說,你去請求后端接口的時候用www.a.com,結果cas登陸成功后的要回調的接口成了www.b.com,這樣你的cas登錄狀態的cookie就附著在www.b.com的域名上了,然后當你再發起www.a.com的請求的時候,發現你根本帶不上cas下來的cookie,因為域不同。
這種情況通常發生在反向代理的時候,前端發起ajax請求代理服務器www.a.com,代理服務器發起請求到www.b.com,這時候就容易導致域名不一致,請一定要注意這點。
另外,對于當前前后端分開部署的情況,location.href中,service的回調接口不能直接寫后端地址(相當于www.b.com),而應該寫www.a.com,讓代理服務器去訪問www.b.com,這樣才能保持cookie的域的一致性!!!!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/52486.html
摘要:這種情況通常發生在反向代理的時候,前端發起請求代理服務器,代理服務器發起請求到,這時候就容易導致域名不一致,請一定要注意這點。 寫在最前 前后端分離其實有兩類: 開發階段使用dev-server,生產階段是打包成靜態文件整個放入后端項目中。 開發階段使用dev-server,生產階段是打包成靜態文件放入單獨的靜態資源服務器中,如nginx。 這兩種方案最大的區別就是生產階段。由于第...
摘要:這種情況通常發生在反向代理的時候,前端發起請求代理服務器,代理服務器發起請求到,這時候就容易導致域名不一致,請一定要注意這點。 寫在最前 前后端分離其實有兩類: 開發階段使用dev-server,生產階段是打包成靜態文件整個放入后端項目中。 開發階段使用dev-server,生產階段是打包成靜態文件放入單獨的靜態資源服務器中,如nginx。 這兩種方案最大的區別就是生產階段。由于第...
摘要:最重要的兩點請求跨域的時候,默認不會攜帶。通常是這樣的前端發起,后端接受請求并執行,前端接受相應并發起,請求重定向后的頁面,其中不存在跨域問題。 最重要的兩點: ajax請求跨域的時候,默認不會攜帶cookie。 請求分為普通請求(HttpRequest)和Ajax請求(XMLHttpRequest) 先屢一下跨域CAS認證的流程: 前端發起ajax請求,請求首先被跨域Filter...
摘要:最重要的兩點請求跨域的時候,默認不會攜帶。通常是這樣的前端發起,后端接受請求并執行,前端接受相應并發起,請求重定向后的頁面,其中不存在跨域問題。 最重要的兩點: ajax請求跨域的時候,默認不會攜帶cookie。 請求分為普通請求(HttpRequest)和Ajax請求(XMLHttpRequest) 先屢一下跨域CAS認證的流程: 前端發起ajax請求,請求首先被跨域Filter...
摘要:目前正在寫一個微信公眾號的小項目,記錄一下遇到的問題和解決方法主要是前端。前端提交時使用,在后端再取出對應的微信支付看了下文檔,以前是需要用喚起支付,而現在則是把微信內置到了微信的瀏覽器中。 目前正在寫一個微信公眾號的小項目,記錄一下遇到的問題和解決方法(主要是前端)。內容持續更新中~ 主要實現 前后端分離前端為 SPA 單頁面使用微信的JSSDK微信支付 技術方案 后端使用 php ...
閱讀 1181·2021-09-22 15:24
閱讀 2285·2019-08-30 15:44
閱讀 2615·2019-08-30 10:55
閱讀 3355·2019-08-29 13:25
閱讀 1639·2019-08-29 13:09
閱讀 1391·2019-08-26 14:05
閱讀 1379·2019-08-26 13:58
閱讀 1985·2019-08-26 11:57