1. 程式人生 > >chrome禁止三方cookie,網站登入不了怎麼辦

chrome禁止三方cookie,網站登入不了怎麼辦

## 背景 新版chrome(80+)瀏覽器預設遮蔽所有三方cookie已經不是什麼新聞了,具體原因這裡不去深究,有大量相關文章介紹,由於目前許多網站都依賴三方cookie,因此該特性的推出還是造成了一些的影響,比如收集使用者資訊的廣告商,而且主流的瀏覽器都跟進chrome的策略,已經成為了既定事實,本篇文章主要聚焦於各種解決方案,大家可以針對自身情況採用不同的解決辦法。 ## 限制說明 ### SameSite cookie新增的屬性,取值包括:Lax(預設),None,Strict __1.None__ :將關閉SameSite屬性,前提是必須同時設定Secure屬性(Cookie 只能通過 HTTPS 協議傳送),否則無效; __2.Strict__ :嚴格模式,完全禁止第三方 Cookie,跨站點時,任何情況下都不會發送 Cookie。換言之,只有當前網頁的 URL 與請求目標一致,才會帶上 Cookie; __3.Lax__ :規則稍稍放寬,大多數情況也是不傳送第三方 Cookie,但是導航到目標網址的 Get 請求除外,具體可參考文末連結【1】; 總數所述,要解決我們的問題,要麼都是同一域名,一勞永逸,要麼採用https協議+SameSite=none,或者不用新版瀏覽器,除此之外,好像也沒有什麼辦法了(如果有,請告訴我-_-)。 ## 解決方案 ### 1.chrome設定 這種方式比較簡單,手動禁用瀏覽器的限制功能,可參考文末連結【2】: ### 2.使用低版本瀏覽器 這也是一種解決方式,但是不推薦; ### 3.https協議 + SameSite=None 這主要依賴運維和後端處理了,但是這種方式在以後新版瀏覽器中可能會失效,因為過兩年瀏覽器將全面禁止三方cookie,到時候怎麼設定都不起作用了; ### 4.代理服務 如果上面的方式都不滿足,可以考慮採用node作為請求-應答的中間層,大體設計如圖: ![圖片描述](https://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/samesite/samesite1.jpg) 前端專案和代理服務位於同一個伺服器,協議和域名一致,只是埠不同而已,為什麼要這麼設計呢,便於cookie共享, __因為cookie是不區分協議和埠的,因此只要域名(或者ip)一致,那麼在同一臺電腦上就可以讀取同域名下的cookie__ 。 還需要說明的一點就是跨域問題是瀏覽器的安全策略,對於代理服務和後端服務來說就沒有跨域一說了,而是程序間的通訊。 接下來我們實現一個比較簡單的三方cookie請求示例,其中各個服務的訪問地址如下(都是本地模擬,因此代理和後端服務的ip一致,而真實情況往往不同): 前端專案: `http://127.0.0.1:8000` node代理服務: `http://127.0.0.1:8001` 後端服務: `http://127.0.0.1:8002` #### 示例演示與流程 ![圖片描述](https://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/samesite/process.gif) ![圖片描述](https://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/samesite/samesite2.jpg) #### 1.登入驗證 首先需要輸入正確的使用者資訊獲取cookie,這裡我們使用iframe+postmessage的方式實現跨域登入請求,流程分為: 1.訪問`http://127.0.0.1:8000`,開啟登陸介面,輸入使用者名稱和密碼 2.點選登入,登入頁面通過postMessage將登入資訊傳送給`http://127.0.0.1:8001`頁面,這個頁面獲取登入資訊後呼叫`http://127.0.0.1:8001/login`登入介面 3.請求到node代理服務後端,然後發起對真正的服務後端請求,然後將後端服務的響應返回給前端頁面 4.如果校驗成功,響應頭會攜帶Set-Cookie資訊,在`http://127.0.0.1:8001`的域下寫入cookie,同時`http://127.0.01:8000`也會寫入同樣的cookie #### 2.cookie讀取 ![圖片描述](https://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/samesite/cookie.gif) 成功登入之後,在`http://127.0.0.1:8000`和`http://127.0.0.1:8001`都儲存有cookie,實現了共享,可以通過document.cookie獲取,如果服務端返回的cookie是httponly,這時可以在代理服務層將這個屬性去掉就可以讀取了。 #### 3.查詢資料 發起資訊查詢時,需要攜帶登陸成功後設置的cookie,這裡就不通過iframe+postMessage的方式了,直接呼叫8001的介面服務,但是要注意一點的就是, __由於是跨域的指令碼請求,因此是不會自動攜帶cookie資訊的(即便是在客戶端可以實現cookie共享)__ ,如果設定withcredentials相關屬性,則還是三方cookie跨域的問題,是不容許攜帶cookie的,因此我們需要手動設定一個請求頭 ___cookie(cookie前面加了個下劃線字首)__ ,將cookie帶上去。 8001上的代理獲取到查詢請求時,解析請求頭的_cookie引數,然後重新設定請求頭的cookie引數,再發送給真正的後端服務介面,這時候就可以實現cookie的校驗了。 #### 4.程式碼實現 檔案結構圖 ![圖片描述](https://fulu-common-util.oss-cn-hangzhou.aliyuncs.com/wiki_assets/samesite/samesite3.jpg) 1.) 前端專案 ```

``` 2.) node代理服務 ``` ---`http://127.0.0.1:8001`--- ``` ``` ---代理服務指令碼--- ...... const CORS_HEADER = { 'Access-Control-Allow-Origin': 'http://127.0.0.1:8000', 'Access-Control-Allow-Headers': 'Content-Type, _cookie', 'Access-Control-Allow-Credentials': 'true', }; ...... function sendProxyRequest(req, res) { const { method, headers, url } = req; const chunks = []; if(Object.hasOwnProperty.call(headers, '_cookie')) { // 包含自定義_cookie請求頭,重新設定cookie headers.cookie = headers._cookie; } req.on('data', (chunk) =>
{ chunks.push(chunk); }); req.on('end', () => { const request = http.request({ // 傳送請求到後端服務 host: '127.0.0.1', port: 8002, path:url, method, headers, }, (response) => { res.writeHead(response.statusCode, { ...CORS_HEADER, ...response.headers, }); response.pipe(res); }); request.end(Buffer.concat(chunks).toString()); }); } ``` 3.) 後端服務 ``` ...... const { method, headers, url } = req; const _method = method.toLowerCase(); if (url === '/login' && _method === 'post') { // 登入驗證 const chunks = []; req.on('data', (chunk) =>
{ chunks.push(chunk); }); req.on('end', () => { const result = { code: -1, message: 'login fail', }; const resHeaders = { 'Content-Type': 'text/json', }; const { name, pass } = JSON.parse(Buffer.concat(chunks).toString()); if (name === '123' && pass === 'abc') { // 這裡只校驗123&abc這種情況 result.code = 0; result.message = 'login success'; resHeaders['Set-Cookie'] = `sid=abc; Max-Age=${getCookieExpires()};`; // 設定cookie } res.writeHead(200, resHeaders); res.end(JSON.stringify(result)); }); ...... return; } if (url === '/fetchUser' && _method === 'post') { // 使用者資訊查詢 if (req.headers.cookie === 'sid=abc') { // 校驗cookie res.writeHead(200, { 'Content-Type': 'text/json', }); ...... } else { res.writeHead(401); res.end(); } return; } ``` ## 總結 第四種方法可以不修改後端服務,微調前端專案即可,因此對於現有專案改造成本較低,但是需要維護一個node服務代理,上面的示例演示了http協議,對於https站點,只需要稍微修改下node代理服務即可(https模組+根證書),最後再說一點,在前後端分離模式下,開發過程中遇到這樣的問題,可以設定webapck的服務代理,具體可參考資料【4】,本文就講到這裡,大家如果有更好的解決方案,歡迎留言交流。 參考資料 【1】http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html 【2】https://www.cnblogs.com/Summer6/p/11671204.html 【3】https://juejin.im/post/6844904128557105166 【4】https://www.yuque.com/mdtvv0/myv5bw/es2oeo 福祿ICH·架構組 福