平常的工作中,前端向後臺發請求的時候,會遇到跨域的問題,這裡就跨域問題進行梳理。
同源策略
講跨域之前,都要講一下同源策略。
同源策略,是瀏覽器最核心的安全功能。
同源策略: 同協議 + 同域名 + 同埠
- 協議:如
HTTP協議
- 域名:如
binnie.qq.com
- 埠:如
80
舉個例子:http://binnie.qq.com:80
與 http://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
是跨域的經典操作,當然,jquery
的ajax
對JSONP
做了處理,前端請求新增dataType:'jsonp'(資料返回格式)
即可發起JSONP
請求。
$.ajax({
url: '/request',
type: 'GET',
dataType: 'jsonp',
success: function(res) {
console.log(res)
}
})
複製程式碼
當然,因為JSONP
是利用<script>
標籤實現的,所以只能傳送get
請求。
2. CORS(跨域資源共享)
CORS
,W3C
的標準,通過對response & request headers
的設定,允許進行跨域的請求。
CORS
請求分為兩種:簡單請求 & 非簡單請求
簡單請求
當請求同時符合以下兩規則時,請求為簡單請求。
- 請求型別為:
HEAD | GET | POST
- 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
欄位。
CORS
與JSONP
的對比這裡也可以看出來,CORS
支援的請求型別更加豐富。
3. postMessage
postMessage
為HTML5
的新特性,支援iframe
之間互相傳送資訊,由於iframe
之間可以不同域,所以postMessage
也是跨域的一種實現。
4. window.name
window.name
與postMessage
的跨域其實是同種型別,在頁面中,父頁面與iframe
都是共享同一個window.name
的,並且都有讀寫許可權。
5.document.domain
document.domain
可以設定為父域,但是不能設定為其他。
6.webSocket
webSocket
,瀏覽器不對其做同源限制,所以直接就可以跨域。
寫在最後
前後臺對接的時候,語氣好的話可能不用跨域,當然跨域的問題也經常會出現,熟悉之後會發現跨域請求還是很容易實現的。