前端遇到302重定向問題處理方式及設置第三方Cookie研究
前端遇到302處理方式以及設置第三方Cookie研究
1、如何解決后端302重定向問題
背景:由于認證中心網(wǎng)關檢測到用戶是未登錄態(tài)情況下的話,會將用戶重定向到認證中心的登錄頁。
? 此時,假如是使用Oauth2協(xié)議,登錄成功后,前端需要帶著登錄成功的信息(jwt),訪問/Oauth2/1/authorize接口,此時該接口將會重定向回redirect_uri的地址,這個時候的重點在于如何訪問/Oauth2/1/authorize接口。
我們列舉一下幾種請求方式:
- Ajax
- Fetch
- Location.href
- Form表單
- Nvigator.sendBeacon
技術 | 特點 | 是否可用 |
---|---|---|
Ajax(XMLHttpRequest ) | 不重新加載整個頁面的情況下,可以與服務器交換數(shù)據(jù)并更新部分網(wǎng)頁內(nèi)容,捕獲不到302狀態(tài)碼,并且假如后端響應頭的Location返回的路徑與Ajax的Referer不一致的話,會產(chǎn)生跨域報錯。 | × |
Fetch | 針對 302 的情況,其實 fetch 有個特殊的配置叫 redirect,可以捕獲302的部分內(nèi)容。但是非常可惜的是,目前 response 的內(nèi)容非常有限,僅能捕獲到該接口訪問是否存在302,捕獲不到302的具體跳轉(zhuǎn)路徑,并且也不能跟隨跳轉(zhuǎn)。 | × |
Location.href | 瀏覽器地址直接跳轉(zhuǎn)接口,默認采用Get請求,不存在跨域問題,瀏覽器也可以直接跟隨后端產(chǎn)生的302重定向。 | √ |
Form表單 | 通過刷新整個頁面進行訪問請求,可使用Get/Post方法,也可以跟隨后端進行的302跳轉(zhuǎn),缺點是需要新建一個表單,操作比較復雜。 | √ |
Nvigator.sendBeacon | navigator.sendBeacon() 方法可用于通過 HTTP POST 將少量數(shù)據(jù)異步傳輸?shù)?Web 服務器。同時避免了用傳統(tǒng)技術(如:XMLHttpRequest)發(fā)送分析數(shù)據(jù)的一些問題。缺點是僅支持POST請求,以及是異步操作,也不跟隨后端302。 | √ |
根據(jù)以上方式做一個測試:
我們做一個302重定向的接口,重定向的路徑是分別為http://localhost:3000/login和https://www.baidu.com,客戶端的域名為localhost,情況如下圖:
假如采用常用的Ajax,例如Axios或者Fetch,前端代碼以及效果如下:
1、Axios:當點擊按鈕時候,調(diào)用/redirectTo接口,后端返回302重定向,此時我們可以看到,重定向后的地址并不是按照預期在瀏覽器上跳轉(zhuǎn),而是重新用XHR請求重定向之后的地址,此時由于接口請求頭的Referer為localhost:3000與RequestURL:https:www.baidu.com存在跨域問題,故報錯。
那假如后端返回重定向的地址不存在跨域問題呢(此時和客戶端同域)?
此時雖然不存在跨域問題了,但是重定向后的地址因為是使用XHR訪問的,然而你客戶端并沒有開啟Servlet等服務,故接口會返回404NotFound。
綜上所述,使用Ajax處理302是行不通的。
2、采用Fetch,網(wǎng)上對于fetch眾說紛紜,經(jīng)測試,fetch也不能跟隨302跳轉(zhuǎn)頁面,會將請求后的報文體返回回來。
3、使用Location.href,無論重定向后的地址是否跨域,均可以成功重定向。
4、使用form表單做302跳轉(zhuǎn),也是可以成功的,但是需要創(chuàng)建一個虛擬節(jié)點,處理起來較為復雜。
5、Nvigator.sendBeacon,由于該方法必須采用Post,故更改一下后端代碼。雖然使用該方法沒有跨域問題,但是由于是異步方法,并不會讓瀏覽器跟隨302操作。
總結:使用Location.href直接訪問接口
上述五種方式,Ajax和Fetch皆是異步請求,不能跟隨瀏覽器302的操作,并且還獲取不到接口返回的Location等信息,所以不采用。Form表單可以使用,功能也較為全面,但是實現(xiàn)方式較為復雜,且Form表單通常用于表單內(nèi)容提交,與場景語義不符,故Pass**。Location.href的方式即以Get請求直接使用瀏覽器訪問該接口,參數(shù)攜帶方便,也能跟隨重定向操作,故采用。**
附上代碼:
import serviceInstance from "../../services"; // const res = serviceInstance({ // url: "/redirectTo", //不用引入,直接在api后面接接口 // method: "get", // data: {}, // }); // console.log(res); function Home() { const setRedirect = () => { const res = serviceInstance({ url: "/redirectTo", //不用引入,直接在api后面接接口 method: "get", data: {}, }); console.log(res); }; const useFetchSetRedirect = () => { const res = fetch("/redirectTo",{ method: "get", redirect:'follow' }); console.log(res); }; const useLocationRedirect = () => { window.location.href = "http://localhost:8080/redirectTo" }; const useFormData = ()=>{ const form = document.createElement("form"); form.action = "http://localhost:8080/redirectTo"; document.getElementById("container").appendChild(form); form.submit(); document.getElementById("container").removeChild(form); } const useSendBeacon = ()=>{ navigator.sendBeacon("http://localhost:8080/redirectToPost") } return ( <div id="container"> <button onClick={setRedirect}>測試Axios重定向</button> <button onClick={useFetchSetRedirect}>測試Fetch重定向</button> <button onClick={useLocationRedirect}>測試location重定向</button> <button onClick={useFormData}>測試Form表單重定向</button> <button onClick={useSendBeacon}>測試SendBeacon重定向</button> </div> ); } export default Home;
package com.xuan.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @RestController public class TestController { @GetMapping("/redirectTo") void testRedirect(HttpServletResponse response) throws IOException { String testUrl = "https://www.baidu.com"; // String testUrl2 = "http://localhost:3000/login"; response.sendRedirect(testUrl); } @PostMapping ("/redirectToPost") void testRedirectPost(HttpServletResponse response) throws IOException { String testUrl = "https://www.baidu.com"; // String testUrl2 = "http://localhost:3000/login"; response.sendRedirect(testUrl); } }
2、子系統(tǒng)(接入認證中心的系統(tǒng))如何接入認證中心,獲取登錄態(tài)?
背景:由于子系統(tǒng)和認證中心是不同的系統(tǒng),在認證中心登錄成功后重定向回子系統(tǒng)的過程中,如何得知已登錄的登錄態(tài)呢?
此時我們在重定向的過程中,需要傳遞信息給其他系統(tǒng),僅有三種方式(據(jù)我所知):
瀏覽器地址欄攜帶
Cookie
Window.postMessage
實現(xiàn)方式 特點 是否可用 瀏覽器地址欄攜帶 優(yōu)點:操作方便,無同域限制 ; 缺點:信息完全暴露在地址欄,安全性不高 √ Cookie 優(yōu)點:在設置Cookie的時候可以通過Domain、Path、SameSite等字段,將Cookie設置在需要獲取的應用上 ,安全性以及準確性較高; 缺點:遵循瀏覽器的同源限制,設置的Cookie僅在本域名或者子域名下共享。 √ Window.postMessage 是一種瀏覽器提供的跨域傳輸信息方式,在認證中心完成登錄操作,拿到Token的話,可以使用該方式傳輸信息。但是與使用場景不符,故Pass掉 ×
總結: 瀏覽器地址欄攜帶和Cookie均可以完成302后傳遞信息(assess-token或者code)的操作,其中各有優(yōu)缺點,應根據(jù)項目需要以及具體情況具體采用不同的方式。同域建議采用Cookie,不同域推薦采用瀏覽器地址欄攜帶。
- 子系統(tǒng)與認證中心同域
采用Cookie方式可以較為簡單地實現(xiàn)單點登錄,設置Cookie(access-token)到具體的域(Domain),通過Path去限制相應的系統(tǒng),這樣可以發(fā)揮認證中心實現(xiàn)單點登錄的效果。但是瀏覽器必須把Cookie打開,以及應對Cookie具體設置對應的條件,以防其他系統(tǒng)通過CSRF等手段,獲取到用戶信息。
- 子系統(tǒng)與認證中心跨域
建議采用瀏覽器地址欄攜傳遞信息,這個時候考慮到安全問題,不應該采用明文的形勢將access-token放到地址欄,而是將重定向后攜帶的code通過地址欄傳輸回去子系統(tǒng),然后子系統(tǒng)通過這個code調(diào)用接口獲取access-token(采用空白頁的方式)。
3、拓展:302跳轉(zhuǎn)如何SetCookie到對應的系統(tǒng)。
背景:出現(xiàn)在本地聯(lián)調(diào)的情況,后端設置重定向到對應的系統(tǒng),并SetCookie到開發(fā)環(huán)境,設置Domain為開發(fā)環(huán)境的ip/域名,但是發(fā)現(xiàn)Cookie并未設置上。
故排查原因,發(fā)現(xiàn)是因為重定向的接口的請求頭中Host與后端設置的Domain不一致,Cookie被屏蔽掉了。報錯如下圖:
為此我做了個測試,模擬設置Cookie到百度的網(wǎng)站上:
首先設置Host文件(127.0.0.1 test.baidu.com),模擬百度的域名到本機ip;
然后在后端代碼重定向到www.baidu.com,同時設置cookie到baidu.com這個域名上。
@GetMapping("/setCookieRedirect") void testCookieRedirect(HttpServletResponse response) throws IOException { String testUrl = "http://baidu.com"; Cookie cookie = new Cookie("testCookie","test"); cookie.setDomain("baidu.com"); cookie.setMaxAge(43200); cookie.setSecure(false); cookie.setHttpOnly(false); response.addCookie(cookie); response.sendRedirect(testUrl); }
重點:前端調(diào)用該后端接口時,不可以用localhost調(diào)用,而是用test.baidu.com這個域名調(diào)用,如下:
const testThirdCookie = ()=>{ window.location. }
最后成功設置Cookie到百度上,效果如下:
總結:關鍵就是請求頭的host與設置的Domain對象域名得一致,或者父級域名包括子級域名即可。
const testThirdCookie = ()=>{ window.location. }
最后成功設置Cookie到百度上,效果如下:
總結:關鍵就是請求頭的host與設置的Domain對象域名得一直,或者父級域名包括子級域名即可。
總結
到此這篇關于前端遇到302重定向問題處理方式及設置第三方Cookie研究的文章就介紹到這了,更多相關前端302處理及設置第三方Cookie內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
最新WebStorm2020.2注冊碼永久激活(激活到2089年)
JetBrains旗下有多款編譯器工具(如:IntelliJ、WebStorm、PyCharm等)在各編程領域幾乎都占據(jù)了壟斷地位。今天給大家?guī)淼氖菍ebStorm最新版激活至2089年2020-09-09基于ChatGPT使用AI實現(xiàn)自然對話的原理分析
ChatGPT是當前自然語言處理領域的重要進展之一,可以生成高質(zhì)量的文本,可應用于多種場景,如智能客服、聊天機器人、語音助手等。本文將詳細介紹ChatGPT的原理、實戰(zhàn)演練和流程圖,幫助讀者更好地理解ChatGPT技術的應用和優(yōu)勢2023-05-05如何集成Elasticsearch到django restful
在Django項目中集成Elasticsearch可通過Haystack實現(xiàn),Haystack作為Django插件提供搜索接口,Elasticsearch作為后端搜索引擎存儲檢索數(shù)據(jù),Haystack支持多種搜索引擎,易于切換且不需改動代碼,本文給大家介紹如何集成Elasticsearch到django restful,感興趣的朋友一起看看吧2024-09-09鴻蒙NEXT元服務之如何利用App?Linking實現(xiàn)無縫跳轉(zhuǎn)與二維碼拉起
本文介紹了如何使用AppLinking技術實現(xiàn)元服務之間的無縫跳轉(zhuǎn),并通過生成二維碼的方式快速拉起元服務,從而簡化用戶操作流程,增強應用的互動性和推廣效率,感興趣的朋友跟隨小編一起看看吧2024-11-11Delphi 本地路徑的創(chuàng)建、清空本地指定文件夾下的文件
這篇文章主要介紹了Delphi 本地路徑的創(chuàng)建、清空本地指定文件夾下的文件,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08