淺談先後端分離中的跨資源共享(CORS)


原文地址html


簡介

當下不管大廠小廠的先後端開發模式都是先後端分離。之前遇到經過jsonp解決跨域的方式也漸漸的淡出的工程中(不瞭解jsonp的能夠看JSONP跨域請求+簡答實現百度搜索)。當前端請求一個接口的時候就會引發跨域,可是當下的前端構建工具都有相應的解決方案,好比webpackweb-dev-server這個插件,就能很簡單的啓一個本地的服務,而後發請求的時候經過啓的本地服務去發送請求,這樣就解決跨域問題的一部分了。這種狀況下客戶端代碼和正常非跨域請求一摸同樣,不用作任何改變。前端

上面說的方法只是解決了一部分,還有一部分我想你們可能或多或少的會遇到過,就是在登陸場景的時候。服務端的response裏面有set-cookie這個字段,在客戶端中設置cookie,cookie裏面可能包含着的seesionID表示的當前登陸的用戶/當前的登陸狀態(對這方面不理解的能夠看經過cookie和session讓http協議變得有狀態)。當用戶已經登陸而且訪問其餘頁面的時候,服務端會經過cookie中的信息去校驗用戶登陸狀態,若是請求中沒有攜帶身份信息或者身份信息過時(服務端返回401/403)就會跳轉到登陸界面。這種狀況若是在先後端聯調的時候比較麻煩,由於上面方法解決的跨域是不會攜帶cookie的。目前有兩種方法去解決這個:vue

  1. 在登陸以後拿到session/token每一個請求都默認加上這個值(寫死在代理中)
  2. 在請求中增長withCredentials,服務端要設置對應的幾個響應頭,可是對服務端改動比較多。

綜上: CORS的主要任務都落在服務端,可是若是爲了聯調服務端的開發代碼和生產代碼有區別,他們確定是會不搞的。webpack

背景

今天組裏的實習生在使用Axios去驗證登陸的時候遇到了跨域的問題(前端是vue, 後端是Spring boot)。 通常的請求經過前端設置代理,服務端設置Access-Control-Allow-origin:\*就能夠了,可是登陸時候響應頭裏面要去set cookie就遇到了問題,結果因爲對CORS跨域理解的不是很深入,對預檢請求不是很瞭解,就在axios的issues裏面去搜,經過Axios doesn't send cookies with POST and data定位到了問題,原來他後端寫的攔截器裏面自動把OPTIONS這個請求給過濾掉了,沒有讓走到後面的流程。ios

什麼是CORS

CORS的出現是爲了解決因爲瀏覽器的同源策略帶來的請求跨域問題。git

「跨資源共享(Cross-Origin Resource Sharing(CORS))是經過HTTP Response header來告訴瀏覽器,讓運行在一個origin(domain)上的web應用被容許訪問來自不一樣源服務器上指定的資源的一種機制。」github

簡單來講: CORS就是經過設置請求的響應頭(能經過開發人員控制的基本都是服務端的響應頭,客戶端的也會有對應的請求頭,但通常不會是開發人員去控制的,後面會仔細說)去控制是否容許某個origin的某個/些請求跨域。web

CORS的功能

CORS標準新增了一組HTTP首部字段,容許服務端聲明哪些源站經過瀏覽器有權訪問哪些資源。對於能對服務器產生反作用的HTTP非簡單請求(non-simple request)(特別是除了GET請求之外的請求),瀏覽器必須首先發送一個方法爲OPTIONS的一個預檢請求(preflight request)來獲取服務器是否容許該請求跨域。服務器獲得確認以後,才發起真正的HTTP請求。在預檢請求中,服務端也能夠通知客戶端是否要攜帶Credentials.json

CORS的三種請求

簡單請求

某些請求是不會觸發預檢查請求的,這些請求被成爲簡單請求(simple request)。 若是一個請求知足下列的全部條件就能夠被稱爲簡單請求:axios

  1. 使用下列的方法之一:
  • GET
  • HEAD
  • POST
  1. 除了瀏覽器自動設置的頭,只能設置Fetch 規範容許設置的「CORS安全請求頭
  1. Content-Type的值只限於下面幾個(注意沒有application/json,如今post的請求常用這個,因此當發post請求的時候會觸發預檢請求不要意外):
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain
  1. 請求中的任意XMLHttpRequestUpload對象均沒有註冊任什麼時候間監聽器。XMLHttpRequestUpload對象可使用*XMLHttpRequest.upload *屬性訪問。
  2. 請求中沒有使用ReadableStream對象

預檢請求

不知足上面定義的簡單請求,都會發送預檢請求。

好比瀏覽器要發送一個POST請求,content-Typeapplication/json,新增一個request header爲X-TEST

預檢請求的步驟:

  1. 發送一個method爲OPTIONS的請求,這個請求的目的是
  • 向服務器請求是否支持實際請求發送的方法(是否支持post方法)
  • 向服務器請求是否支持實際請求新增的header或者不知足簡單請求的header
  1. 若是服務器接受並正確返回就發送實際的post,而且能帶上相應的header

攜帶credentials的請求

對於跨域(發生CORS)的請求默認是不會帶上憑證信息(credentials)的,若是要發送憑證信息(credentials)就須要設置對應的標識位。

請求:

  • 請求中要設置withCredentials爲true。

響應:

  • Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin的值再也不是通配符*,應該是單一的origin。

HTTP規範規定Access-Control-Allow-Origin不能是通配符*而且只能是單一的origin。這是由於若是能設置多個的話,證實該服務器就能接受多個域名下面的cookie,這是很危險的。

CORS中的請求頭和響應頭

響應頭

Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

origin參數的值制定了容許訪問服務器資源的外域URI。對於不須要攜帶身份憑證的請求,服務器能夠指定這個字段的值爲通配符*,表示容許來自全部域的請求。

Access-Control-Expose-Headers

該頭信息服務器把容許瀏覽器訪問的頭放入白名單,例如:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header 在跨域訪問的時候,XHR對象的getResponseHeader()只能拿到一些最基本的響應頭。

Access-Control-Max-Age

指定了預檢請求(preflight)請求的結果能被緩存多久(秒爲單位)。

Access-Control-Allow-Credentials

當瀏覽器的credentials設置爲true時,是否容許瀏覽器讀取response的內容

Access-Control-Allow-Methods

做爲預檢請求的響應頭,指明瞭實際請求所容許的HTTP方法。

Access-Control-Allow-Headers

用於預檢請求的響應。其指明瞭實際請求中容許攜帶的首部字段。 以逗號分割。

請求頭

這些字段通常無需手動設置。

Origin

預檢請求或實際請求的源站。

不包含任何路徑,只是服務器的名稱。(不論是否爲跨域,這個字段都被髮送。)

Access-Control-Request-Method

用於預檢請求。其做用是,將實際請求所使用的 HTTP 方法告訴服務器。

參考

相關文章
相關標籤/搜索