2020-03-20

說說跨域?面試官聽完之后露出了滿意的笑容??

2020年3月18日
《每日一題系列??》
作者:王二狗
博客:掘金、思否、知乎、簡書、CSDN
點贊再看,養(yǎng)成習(xí)慣,每日一題系列會一直更新下去,你們的支持是我持續(xù)分享的最大動力??

同源策略

在說跨域之前,首先需要了解的一個概念就是”同源策略“。

什么是源?

源=協(xié)議+域名+端口號。

如果兩個url的協(xié)議、域名、端口號完全一致,那么這兩個url就是同源的。

我們可以通過window.originlocation.origin得到當(dāng)前源。

https://wang.com
https://ergou.com
//不同源,域名不一致(記?。褐挥型耆荒R粯硬潘阃矗?
http://wang.com/index.html
http://wang.com/server.php
//同源

localhost 調(diào)用 127.0.1 
//不同源

什么是同源策略

同源策略即:不同源之間的頁面,不準(zhǔn)互相訪問數(shù)據(jù)。

瀏覽器規(guī)定:如果JS運行在源A里,那么就只能獲取源A的數(shù)據(jù),不能獲取源B的數(shù)據(jù),即不允許跨域。

假設(shè) wang.com/index.html引用了ergou.com/1.js,那么就說1.js運行在源wang.com

注意,這和ergou.com沒有關(guān)系,雖然1.js是從它那里下載的.

所以1.js就只能獲取wang.com的數(shù)據(jù),這就是瀏覽器的功能,瀏覽器就是故意這樣設(shè)計的。

為什么會有同源策略

之所以需要使用同源策略,就是為了保護用戶的隱私。

以微信為例,源為 https://user.weixin.com,假設(shè)當(dāng)前用戶已經(jīng)登錄,并且AJAX請求 /friends.json 可以獲取用戶好友列表。

這個時候黑客來了,他把 https://user-winxin.com分享給你,實際上這是一個釣魚網(wǎng)站,你點開這個網(wǎng)頁,這個網(wǎng)頁也請求你的好友列表 https://user.weixin.com/friends.json。

請問,這個時候你的好友列表是不是就被黑客給偷走了?

問題的根源

之所以會出現(xiàn)這個問題,其根源就在于無法區(qū)分發(fā)送者。

微信里面的JS和黑客的JS發(fā)送到請求幾乎沒有區(qū)別(referer區(qū)別)

但是如果后臺的開發(fā)者沒有檢查 referer,那么就完全沒有區(qū)別。

所以,如果沒有同源策略,任何頁面都能偷走微信里面的數(shù)據(jù),甚至是支付寶里面的余額。

安全原則

有的小伙伴可能會問,既然referer有區(qū)別,那檢查referer不就好了?

安全原則:安全鏈條上的強度取決于安全鏈條上最弱的一環(huán)。

同時,萬一這個網(wǎng)站的后端開發(fā)者是一個傻叉呢?

所以瀏覽器應(yīng)該主動預(yù)防這種偷數(shù)據(jù)的行為。

總之,為了保護用戶的隱私,瀏覽器設(shè)置了嚴格的同源策略。

如果瀏覽器不限制跨域,一定是這個瀏覽器出現(xiàn)了bug。

跨域

什么是跨域

跨域,即瀏覽器試圖執(zhí)行其他網(wǎng)站的腳本。但是由于同源策略的限制,導(dǎo)致我們無法實現(xiàn)跨域。

關(guān)于跨域的幾個問題

  • 為什么a.wang.com訪問wang.com也算跨域?

因為歷史上,出現(xiàn)過不同的公司共用域名,a.wang.comwang.com不一定是同一個網(wǎng)站,瀏覽器謹慎起見,認為這是不同的源。

  • 為什么不同端口也算跨域?

原因同上,一個端口一個公司的情況也不是沒有的。

記?。?/strong>安全鏈條的強度取決于最弱的一環(huán),所有和安全相關(guān)的問題都要謹慎對待。

  • 為什么兩個網(wǎng)站的IP一樣,也算跨域?

原因同上,因為IP也是可以共用的。

  • 為什么可以跨域使用CSS、JS和圖片等?

同源策略限制的是數(shù)據(jù)訪問,我們引用CSS、JS和圖片的時候,其實并不知道其內(nèi)容,我們只是在引用。

CORS跨域

什么是CORS

CORS的全稱是"跨域資源共享"(Cross-origin resource sharing)。 它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

如何理解CORS

如果wang.comergou.com這兩個網(wǎng)站都是我的,我就是想讓zhen.com去訪問ergou.com里面的數(shù)據(jù)應(yīng)該怎么辦呢?

只需要wang.com在響應(yīng)頭里寫ergou.com可以訪問即可。這就是CORS。

實現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實現(xiàn)了CORS接口,就可以跨源通信。

兩種請求

CORS跨域分為兩種請求,一種是簡單請求,另外一種就是復(fù)雜請求。

簡單請求

只要滿足以下條件的就是簡單請求:

  • 請求方式為HEADPOST 或者 GET
  • http頭信息不超出以下字段:Accept、Accept-LanguageContent-Language、 Last-Event-ID、 Content-Type(限于三個值:application/x-www-form-urlencoded、multipart/form-datatext/plain)

簡單請求的實現(xiàn)具體來說就是在信息頭中加入一個Origin字段:

GET /cors HTTP/1.1
Origin: http://wang.com
Host: api.ergou.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0
...

Origin的作用就是用來說明本次請求來自哪個源,服務(wù)器會根據(jù)Origin的值來判斷是否接受本次請求。

如果Origin所表示的源不被服務(wù)器接受,即瀏覽器發(fā)現(xiàn)回應(yīng)的信息頭中沒有Access-Control-Allow-Origin字段,就會自動拋出一個錯誤。

注意:這種錯誤是無法通過狀態(tài)碼識別的,這也是通過CORS實現(xiàn)跨域請求的一個弊端。

如果Origin所表示的源被服務(wù)器端所接受,那么服務(wù)器就會返回如下響應(yīng):

Access-Control-Allow-Origin: http://api.ergou.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
  • Access-Control-Allow-Origin :該字段是必須的。它的值要么是請求時Origin字段的值,要么是一個*,表示接受任意域名的請求
  • Access-Control-Allow-Credentials: 該字段可選。它的值是一個布爾值,表示是否允許發(fā)送Cookie。默認情況下,Cookie不包括在CORS請求之中。設(shè)為true,即表示服務(wù)器明確許可,Cookie可以包含在請求中,一起發(fā)給服務(wù)器。這個值也只能設(shè)為true,如果服務(wù)器不要瀏覽器發(fā)送Cookie,刪除該字段即可。(注意:如果要發(fā)送cookie,不僅要進行上述的設(shè)置,還要在AJAX請求中設(shè)置withCredentials屬性)
  • Access-Control-Expose-Headers:該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。

復(fù)雜請求

所謂復(fù)雜請求,即不滿足上述條件的請求就是復(fù)雜請求。

比如請求的方法是PUTDELETE,或者Content-Type字段的類型是application/json。

復(fù)雜請求首先會發(fā)起一個預(yù)檢請求,該請求是 option 方法的,通過該請求來知道服務(wù)端是否允許跨域請求。

var url = 'http://api.wang.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();

上面的請求就是一個復(fù)雜請求,當(dāng)瀏覽器發(fā)現(xiàn)這是一個復(fù)雜請求之后,就會主動發(fā)出一個預(yù)檢請求,詢問服務(wù)器是否允許本次請求。

服務(wù)器收到預(yù)檢請求之后,檢查了Origin、Access-Control-Request-MethodAccess-Control-Request-Headers字段以后,確認允許跨源請求,才會做出相應(yīng)的回應(yīng)。

Access-Control-Allow-Origin: http://api.wang.com
Content-Type: text/html; charset=utf-8

CORS存在的問題

不支持IE8/9,如果要在IE8/9使用CORS跨域需要使用XDomainRequest對象來支持CORS

解決方法二:JSONP

什么是JSONP

我們在跨域的時候由于當(dāng)前的瀏覽器不支持 CORS 或者因為某些條件不支持 CORS,我們必須使用另外一種方式來跨域,于是我們就請求一個 JS 文件,這個 JS 文件會執(zhí)行一個回調(diào),回調(diào)里面就有我們需要的數(shù)據(jù)。


let script = document.createElement('script');

script.src = 'http://www.wang.cn/login?username=wang&callback=callback';

document.body.appendChild(script);

function callback(res) {
  console.log(res);
}

回調(diào)函數(shù)的名字是什么?

回調(diào)的名字是可以隨機生成的的一個隨機數(shù),我們把這個名字當(dāng)成 callback 的參數(shù)傳給后臺,后臺會把這個函數(shù)再次返回給我們并執(zhí)行

JSONP跨域優(yōu)點

  • 兼容ie
  • 可以跨域

JSONP跨域缺點

  • 由于是 script 標(biāo)簽,所以讀不到 ajax 那么精確的狀態(tài),不知道狀態(tài)碼是什么,也不知道響應(yīng)頭是什么,它只知道成功和失敗。
  • 不支持post(因為是 script 標(biāo)簽,所以只支持 get 請求)

告誡自己,即使再累也不要忘記學(xué)習(xí),成功沒有捷徑可走,只有一步接著一步走下去。 共勉!

文章中如有不對的地方,歡迎小伙伴們多多指正。

謝謝大家~ ??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 題目1.什么是同源策略? 同源策略(Same origin Policy): 瀏覽器出于安全方面的考慮,只允許與本...
    FLYSASA閱讀 1,876評論 0 6
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實...
    Yaoxue9閱讀 1,412評論 0 6
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實...
    他方l閱讀 1,134評論 0 2
  • 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實...
    HeroXin閱讀 955評論 0 4
  • 1. 什么是跨域 跨域,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScri...
    cbw100閱讀 6,479評論 2 86

友情鏈接更多精彩內(nèi)容