React項目中fetch實現跨域接收傳遞session的解決方案


  本次項目使用了react框架,同時使用fetch取代ajax作為獲取接口數據的交互方法。本以為過程中應該不會有什么磕絆,沒想到遇到了session丟失這個讓人甚是苦惱的問題。期間本想換種方法來對接接口,但轉念一想如果每次遇到問題都選擇逃避,那么以后的編碼之路只能越走越窄,所以還是決定堅持下去。好在經過一整天的摸索,總算是成功攻克了這個難關,下面就對這次問題的解決做個總結。

  首先,為什么會出現postman接口調試正常而程序里fetch調用卻出現session丟失的問題?

  回顧fetch本身的特性——默認情況下, fetch在服務端不會發送或接收任何 cookies!!

  再來看session——當客戶端第一次請求服務器的時候,服務器生成一份session保存在服務端,將該數據(session)的id以cookie的形式傳遞給客戶端;以后的每次請求,瀏覽器都會自動的攜帶cookie來訪問服務器(session數據id)。

  這就意味着fetch這種方法如果想要攜帶cookie,需要經過特殊的處理——credentials: "include",代碼如下:

 1 fetch("https://ip:端口/api/Values/GetVerifyCode", {
 2 
 3             method: "GET",
 4 
 5             credentials: 'include',
 6 
 7             mode: 'cors',
 8 
 9             headers: {
10 
11                 'Content-Type': 'application/json'
12 
13             }
14 
15         }).then(res => res.json())
16 
17             .then(res => {
18 
19                 if (res.code == 200) {
20 
21                     //xxxxxxx
22 
23                 }
24 
25             });

  這里還涉及到前后台分離時經常遇到的跨域問題(mode: 'cors'),什么是跨域?瀏覽器從一個域名的網頁去請求另一個域名的資源時,域名、端口、協議任一不同,都是跨域。在前后端分離的模式下,前后端的域名是不一致的,此時就會發生跨域訪問問題。在請求的過程中我們要想回去數據一般都是post/get請求,所以..跨域問題就出現。這是出於瀏覽器的同源策略限制。(節選自https://zhuanlan.zhihu.com/p/425855609

  后台以.netcore webapi為例,需要在Startup.cs里配置好Cors,代碼如下:

  ConfigureServices里:

 1 services.AddCors(options =>
 2 
 3             {
 4 
 5                 options.AddPolicy("any", builder =>
 6 
 7                 {
 8 
 9                     builder.AllowAnyOrigin() 10 
11                     .AllowAnyMethod()
12 
13                     .AllowAnyHeader();
14 
15                 });//跨域請求
16 
17             });

  Configure里:

1 app.UseCors("any");//跨域請求

  這里要注意的是如果你在服務器端的web.config里已經配置過跨域相關參數了,這里在加的話程序運行時會報錯(”*,*”…意思就是重復配置跨域參數),要使用credentials: 'include',需要將上面的允許來自所有域的跨域請求訪問改為只允許特定域訪問,同時允許Credentials,如下:

1 builder.WithOrigins("http://ip:端口", "http://localhost:3000")
2 
3                     .AllowAnyMethod()
4 
5                     .AllowAnyHeader()
6 
7                     .AllowCredentials();

  本來進行到這里我以為問題已經解決了,在本地調試OK的情況下發布接口,然后調用發布好的服務器接口,卻還是沒有獲取到想要的cookie。以驗證碼為例:一個fetch1生成驗證碼,並把它通過響應標頭帶回來,之后另一個fetch2調接口時把之前帶回來的cookie再通過請求標頭傳到后台進行校驗,打開控制台發現fetch2的請求標頭並沒有cookie值,而fetch1的響應標頭里也沒有Set-Cookie。

  點開末尾的cookie才發現問題的所在:

       

       

  這個SameSite是何許人也?

  SameSite是瀏覽器請求中Set-Cookie響應頭新增的一種屬性,它用來標明這個 cookie 是否是“同站 cookie”,同站 cookie 只能在本域名中使用的cookie,不能作為第三方 cookie。Chrome 51 開始,瀏覽器的 Cookie 新增加了一個SameSite屬性,用來防止 CSRF 攻擊和用戶追蹤。該屬性起初是由Google 起草的一份草案用來來改進 HTTP 協議的。Chrome 計划將Lax變為默認設置。這時,網站可以選擇顯式關閉SameSite屬性,將其設為None (對舊版瀏覽器無效,因為舊版瀏覽器沒有該值) 。不過,前提是必須同時設置Secure屬性(Cookie 只能通過 HTTPS 協議發送),否則無效。(節選自https://www.cnblogs.com/w821759016/p/14595832.html

  了解了這個,便可以很快地對Startup.cs里的services.AddSession做出調整,如下:

 1 services.AddSession(options =>
 2 
 3             {
 4 
 5                 options.Cookie.Name = ".AdventureWorks.Session";
 6 
 7                 options.IdleTimeout = TimeSpan.FromSeconds(1800);//設置session的過期時間
 8 
 9                 options.Cookie.HttpOnly = true;//設置在瀏覽器不能通過js獲得該cookie的值
10 
11                 options.Cookie.SameSite = SameSiteMode.None;
12 
13                 options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
14 
15                 options.Cookie.IsEssential = true;
16 
17             });

  其中,SameSite設為None配合Secure設為SameAsRequest,但要注意這個只允許訪問的接口為https接口,另外將SameSite設置為了Unspecified瀏覽器則會使用默認值lax,是沒有效果的。

  最后,將原先的http接口改為https,至於https認證所需的SSL證書怎么搞這里就不贅述了,將這些完成后再次嘗試調用發布好的接口,fetch1里的響應標頭以及fetch2里的請求標頭就都能在控制台看見了,問題解決!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM