跨域請求

寫不好程式碼的格子襯衫發表於2018-11-04

平常的工作中,前端向後臺發請求的時候,會遇到跨域的問題,這裡就跨域問題進行梳理。

同源策略

講跨域之前,都要講一下同源策略。

同源策略,是瀏覽器最核心的安全功能。

同源策略: 同協議 + 同域名 + 同埠

  • 協議:如 HTTP協議
  • 域名:如 binnie.qq.com
  • 埠:如 80

舉個例子:http://binnie.qq.com:80http://binnie.qq.com:443 是不同源的,因為其埠不同。

講完同源策略,就進入到本文的主題了,如果要在不同的源之間進行通訊,那就要進行跨域,下面講一下跨域常用幾種方法。

1. JSONP

JSONP,跨域的一種經典模式,核心是使用script標籤(因為script標籤支援請求其他源的資料)

正常發起js請求是這樣的,<script>可以直接拉到js的資料並且直接執行。

<script src="/jquery.js"></script>
複製程式碼

JSONP的請求時這樣的,請求地址攜帶callback引數,該引數為資料回撥函式,後臺回的資料格式為cbFun(res),把回包做為函式的引數返回。

<!-- 前端 -->  
<script>
    function cbFun(res) {
        console.log(res)    // 請求拿到的資料
    }
</script>
<script src="/request?callback=cbFun"></script>
複製程式碼

後臺也要配合處理,接收請求時獲取callback引數,回包時將資料包裹為callback的引數中轉為字串返回。

app.use(async ctx => {
    var res = {
        ret: 0,
        msg: 'ok',
        data: 'Hello World'
    }
    ctx.body = ctx.query.callback + '(' + JSON.stringify(res) + ')'
});
複製程式碼

JSONP是跨域的經典操作,當然,jqueryajaxJSONP做了處理,前端請求新增dataType:'jsonp'(資料返回格式)即可發起JSONP請求。

$.ajax({
    url: '/request',
    type: 'GET',
    dataType: 'jsonp',
    success: function(res) {
        console.log(res)
    }
})
複製程式碼

當然,因為JSONP是利用<script>標籤實現的,所以只能傳送get請求。

2. CORS(跨域資源共享)

CORSW3C的標準,通過對response & request headers的設定,允許進行跨域的請求。

CORS請求分為兩種:簡單請求 & 非簡單請求

簡單請求

當請求同時符合以下兩規則時,請求為簡單請求。

  1. 請求型別為:HEAD | GET | POST
  2. Content-Type:text/plain | multipart/form-data | application/x-www-form-urlencoded

傳送CORS請求的時候,瀏覽器會在request headers增加一個Origin欄位,這個欄位就是網站的源(協議+域名+埠)

Origin: 'http://binnie.qq.com'
複製程式碼

伺服器跟Origin對應的是 Access-Control-Allow-Origin,該屬性表示伺服器支援的源是哪些,也可以是*全部支援。

Access-Control-Allow-Origin: 'http://binnie.qq.com'
複製程式碼

由於請求是跨域的,所以cookie是攜帶不過去的,如果想攜帶本域的cookie,請求中就要多帶一個欄位 withCredentials

withCredentials: true
複製程式碼

伺服器跟withCredentials對應的是Access-Control-Allow-Credentials,表示是否允許攜帶cookie。如果需要新增這個配置,那麼Access-Control-Allow-Origin不允許為*,只能設定為確定的域名

Access-Control-Allow-Origin: 'http://binnie.qq.com'
Access-Control-Allow-Credentials: true
複製程式碼

非簡單請求

不符合簡單請求的,就都是非簡單請求。

非簡單請求與簡單請求不不同之處在於,簡單請求一條請求就搞定,非簡單請求需要兩條。

  • 第一條:預檢請求(OPTIONS)
  • 第二條:真正的請求

預檢請求

預檢請求,作用是先詢問伺服器,之後的請求的域、請求型別、攜帶headers有哪些,伺服器通過之後,就會傳送真正的請求,伺服器不通過則不會發真正的請求。

預檢Headers會攜帶以下幾個資訊

Origin: 'http://binnie.qq.com'              // 源
Access-Control-Request-Method: 'PUT'        // 真正請求的方法
Access-Control-Request-Headers: 'binnie'    // 有擴充套件Header
複製程式碼

伺服器與預檢請求的對應

Access-Control-Allow-Origin: 'http://binnie.qq.com'     // 允許的源
Access-Control-Allow-Methods: 'GET,POST,PUT'            // 允許的請求方法
Access-Control-Allow-Headers: 'binnie'                  // 允許的擴充套件Header
複製程式碼

真正的請求

預檢請求通過之後,其實真正的請求就可以請求成功了,這時request Headers攜帶Origin欄位。

CORSJSONP的對比這裡也可以看出來,CORS支援的請求型別更加豐富。

3. postMessage

postMessageHTML5的新特性,支援iframe之間互相傳送資訊,由於iframe之間可以不同域,所以postMessage也是跨域的一種實現。

4. window.name

window.namepostMessage的跨域其實是同種型別,在頁面中,父頁面與iframe都是共享同一個window.name的,並且都有讀寫許可權。

5.document.domain

document.domain可以設定為父域,但是不能設定為其他。

6.webSocket

webSocket,瀏覽器不對其做同源限制,所以直接就可以跨域。

寫在最後

前後臺對接的時候,語氣好的話可能不用跨域,當然跨域的問題也經常會出現,熟悉之後會發現跨域請求還是很容易實現的。

相關文章