cookie跨域那些事兒

一個請求從發出到返回,需要瀏覽器和服務端的協調配合。瀏覽器要把自己的請求參數帶給服務端,服務端校驗參數之後,除了返回數據,也可能會順便把請求是否緩存,cookie等信息告訴瀏覽器。當請求是跨域請求的時候,這個過程還要複雜一些。接下來咱們就看看跨域會有什麼問題,又需要前後端進行怎樣的配合。

普通跨域

我有一個朋友,叫小王。前端小王和後端同事小馬準備聯調一個登錄的api。假設是/login;小王在把登錄賬號和密碼都準備好之後,愉快的發起了post提交。結果很意外,請求的響應被瀏覽器攔截了,瀏覽器還貼心的在console上拋出了一個錯誤。
image
小王翻譯了一下,原來是被CORS策略攔截掉了。這個策略大概意思是說,服務端如果允許不同origin的請求,那就需要在返回的response header裏面帶上Access-Control-Allow-Origin這個header。否則瀏覽器在拿到響應並發現響應頭裏沒有這個header時,就會把響應給吞掉,而不會交給js進行下一步處理。

小王把這個事情告訴了小馬,然後小馬在返回的header中加上了

Access-Control-Allow-Origin: *

現在小王終於可以拿到返回的結果了。

這裏要注意,瀏覽器不是在請求階段就對請求進行攔截,而是正常發出請求,拿到服務端的響應之後,開始查看響應header裏面有沒有Access-Control-Allow-Origin這個header,如果沒有,響應的結果就不會到js那裏去。

非簡單請求的跨域

後來小王覺得在post中發送表單格式的body太麻煩,希望使用JSON格式的請求體提交。小馬覺得就是幾行代碼的事,就同意了。但是小王改成JSON的消息體之後發現又被CORS攔截了,並拋出了下面的錯誤:
image

在上面的報錯中,我們看到了 preflight 的單詞。那這又是怎麼回事呢?原來,修改請求體之後,這個跨域請求不再是簡單請求了,需要在發起請求之前先進行 preflight 請求。那麼什麼是簡單請求呢?

  • 請求方法包括GET, HEAD, POST
  • response header裏面不能包含cors安全header以外的header。
  • Content-Type 只限於text/plain, multipart/form-data, application/x-www-form-urlencoded

由於json數據的content-type導致這個post請求不再是簡單請求,而對於非簡單請求,之前允許所有域名跨域訪問是被禁止的。所以還是要修改Access-Control-Allow-Origin爲特定的請求域名。在開發模式下,可能是http://localhost:3000之類的。

小馬在重新修改Access-Control-Allow-Origin,小王又拿到了登錄成功的結果。可以聯調下一個api了。

帶cookie的跨域

登錄是基於session的,也就是說,登錄成功後,server會通過set-cookie,將cookie設置到瀏覽器中,這樣,下次訪問同源下的api時,cookie就會被帶上。

然而,奇怪的是,小王發現登錄成功後,調用別的接口,cookie並沒有被帶上,導致server無法識別出用戶信息,最終返回錯誤(狀態碼爲401)。

withCredentials

原來,瀏覽器發起跨域請求的時候,是不會主動帶上cookie的,如果一個請求需要cookie,需要開發者設置一個選項,以fetch api爲例:

fetch('http://baidu.com:3000', {
    // ...
	credentials: true
})

如果使用xhr api來請求,則需要這樣寫:

var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';

function callOtherDomain(){
  if(invocation) {
    invocation.open('GET', url, true);
    invocation.withCredentials = true; // 帶上cookie
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}

小王在設置請求之後又發起了一次請求。卻發現cookie還是沒有帶上去。小王只好在MDN繼續查看資料,發現在set-cookie時需要帶一個sameSite的屬性。

sameSite

sameSite是爲了防止csrf攻擊而產生的屬性,如果不知道啥是CSRF攻擊,可以看我這篇文章。由於我們需要在請求中帶上cookie,所以需要在set-cookie時將cookie的sameSite設置爲none;又由於將sameSite設置爲none時,也需要將Secure設置上,所以請求需要基於https;

小王最後一次請求小馬對api進行了上訴更改,服務器終於認出請求來自誰,並返回了正確的結果,跨域的踩坑之旅算是告一段落。

總結

很多時候,我們可能只會關注請求體是什麼,響應有沒有正確返回,而忽略了header部分。殊不知,header在緩存,web安全,瀏覽器正確解析結果中發揮了重要的作用,比如本文中的一系列Access-Control-Allow-*的header。希望本文能對您理解跨域有所幫助。(本文完)

參考資料

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies#samesite_cookies
https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Authentication

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章