1娜氏、什么是跨域抽活?
瀏覽器遵循同源政策(scheme(協(xié)議)子巾、host(主機)和port(端口)都相同則為同源)。
非同源站點有這樣一些限制:
a茶敏、不能讀取和修改對方的 DOM
b壤靶、不讀訪問對方的 Cookie、IndexDB 和 LocalStorage
c惊搏、限制 XMLHttpRequest 請求贮乳,AJAX 請求不能發(fā)送忧换。
當瀏覽器向目標 URI 發(fā) Ajax 請求時,只要當前 URL 和目標 URL 不同源向拆,則產生跨域亚茬,被稱為跨域請求。跨域請求的響應一般會被瀏覽器所攔截浓恳,注意才写,是被瀏覽器攔截,響應其實是成功到達客戶端了奖蔓。
2、跨域的解決方案
1)CORS
CORS 跨域資源共享(Cross-origin resource sharing)其實是 W3C 的一個標準讹堤,全稱是跨域資源共享吆鹤。它需要瀏覽器和服務器的共同支持。支持所有類型的HTTP請求洲守。
1)若瀏覽器發(fā)送的是個跨域請求疑务,http請求中就會攜帶一個名為Origin的頭表明自己的“位置”,如Origin: http://localhost:5432
2)服務端接到請求后梗醇,就可以根據傳過來的Origin頭做邏輯知允,決定是否要將資源共享給這個源嘍。而這個決定通過響應頭Access-Control-Allow-Origin來承載叙谨,它的value值可以是任意值温鸽,有如下情況:
a、值為*手负,通配符涤垫,允許所有的Origin共享此資源。
b竟终、值為http://localhost:5432(也就是和Origin相同)蝠猬,共享給此Origin。
c统捶、值為非http://localhost:5432(也就是和Origin不相同)榆芦,不共享給此Origin。
d喘鸟、無此頭:不共享給此origin匆绣;有此頭:值有如下可能情況。
3)瀏覽器接收到Response響應后什黑,會去提取Access-Control-Allow-Origin這個頭犬绒。然后根據上述規(guī)則來決定要接收此響應內容還是拒絕
我們需要清楚兩個概念:?簡單請求和非簡單請求。瀏覽器根據請求方法和請求頭的特定字段兑凿,將請求做了一下分類凯力,具體來說規(guī)則是這樣茵瘾,凡是滿足下面條件的屬于簡單請求:
a、請求方法為 GET咐鹤、POST 或者 HEAD
b拗秘、請求頭的取值范圍: Accept、Accept-Language祈惶、Content-Language雕旨、Content-Type(只限于三個值application/x-www-form-urlencoded、multipart/form-data捧请、text/plain)凡涩。
剩下的則是非簡單請求。
簡單請求會自動在請求頭當中疹蛉,添加一個Origin字段活箕,用來說明請求來自哪個源。服務器拿到請求之后可款,在回應時對應地添加Access-Control-Allow-Origin字段育韩,如果Origin不在這個字段的范圍中,那么瀏覽器就會將響應攔截闺鲸。
Access-Control-Allow-Origin字段是服務器用來決定瀏覽器是否攔截這個響應筋讨,這是必需的字段。
Access-Control-Allow-Credentials表示是否允許發(fā)送 Cookie摸恍,對于跨域請求悉罕,瀏覽器對這個字段默認值設為 false,而如果需要拿到瀏覽器的 Cookie立镶,需要添加這個響應頭并設為true, 并且在前端也需要設置withCredentials屬性:
Access-Control-Expose-Headers:可選
CORS請求時蛮粮,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language谜慌、Content-Type然想、Expires、Last-Modified欣范、Pragma变泄。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定恼琼。
非簡單請求
非簡單請求體現(xiàn)在兩個方面:?預檢請求和響應字段妨蛹。
我們以 PUT 方法為例:
當這段代碼執(zhí)行后,非簡單請求的CORS請求晴竞,會在正式通信之前蛙卤,增加一次HTTP查詢請求,稱為預檢請求。這個預檢請求的請求行和請求體是下面這個格式:
預檢請求的方法是OPTIONS颤难,同時會加上Origin源地址和Host目標地址神年,同時也會加上Access-Control-Request-Method(列出 CORS 請求用到哪個HTTP方法)、Access-Control-Request-Headers(指定 CORS 請求將要加上什么請求頭)行嗤。
接下來是響應字段已日,響應字段也分為兩部分,一部分是對于預檢請求的響應栅屏,一部分是對于?CORS 請求的響應飘千。
預檢請求的響應。如下面的格式:
其中有這樣幾個關鍵的響應頭字段:
a栈雳、Access-Control-Allow-Origin: 必選护奈,表示可以允許請求的源,可以填具體的源名哥纫,也可以填*表示允許任意源請求霉旗。
b、Access-Control-Allow-Methods: 表示允許的請求方法列表磺箕。
c、Access-Control-Allow-Credentials: 它的值是一個布爾值抛虫,表示是否允許發(fā)送Cookie松靡。默認情況下,Cookie不包括在CORS請求之中建椰。設為true雕欺,即表示服務器明確許可,Cookie可以包含在請求中棉姐,一起發(fā)給服務器屠列。
d、Access-Control-Allow-Headers: 表示允許發(fā)送的請求頭字段伞矩。CORS請求時笛洛,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language乃坤、Content-Type苛让、Expires、Last-Modified湿诊、Pragma狱杰。如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定厅须。
e仿畸、Access-Control-Max-Age: 預檢請求的有效期,在此期間,不用發(fā)出另外一條預檢請求错沽。
在預檢請求的響應返回后簿晓,如果請求不滿足響應頭的條件,則觸發(fā)XMLHttpRequest的onerror方法甥捺,當然后面真正的CORS請求也不會發(fā)出去抢蚀。
CORS 請求的響應。現(xiàn)在它和簡單請求的情況是一樣的镰禾。瀏覽器自動加上Origin字段皿曲,服務端響應頭返回Access-Control-Allow-Origin。
前端設置:
服務端設置:
服務器端對于CORS的支持吴侦,主要是通過設置Access-Control-Allow-Origin來進行的屋休。如果瀏覽器檢測到相應的設置,就可以允許Ajax進行跨域的訪問备韧。
2)JSONP
雖然XMLHttpRequest對象遵循同源政策劫樟,但是script標簽不一樣,它可以通過 src 填上目標地址從而發(fā)送帶有callback參數的GET 請求织堂,服務端將接口返回數據拼湊到callback函數中叠艳,返回給瀏覽器,瀏覽器解析執(zhí)行易阳,從而前端拿到callback函數返回的數據附较,實現(xiàn)跨域請求并拿到響應。這也就是 JSONP 的原理潦俺。
缺點是只支持get請求拒课,不支持post請求。適合加載不同域名的js事示、css早像,img等靜態(tài)資源。
3)postMessage() 方法
postMessage() 方法用于安全地實現(xiàn)跨源通信肖爵。
接收頁面監(jiān)聽window的message事件就可以卢鹦。為了安全起見,我們利用這時候的MessageEvent對象判斷了一下消息源劝堪,它包括如下幾個屬性:
data:顧名思義法挨,是傳遞來的message
source:發(fā)送消息的窗口對象
origin:發(fā)送消息窗口的源(協(xié)議+主機+端口號)
4)Nginx
Nginx 是一種高性能的反向代理服務器,可以用來輕松解決跨域問題幅聘。
正向代理幫助客戶端訪問客戶端自己訪問不到的服務器凡纳,然后將結果返回給客戶端。
反向代理拿到客戶端的請求帝蒿,將請求轉發(fā)給其他的服務器荐糜,主要的場景是維持服務器集群的負載均衡,換句話說,反向代理幫其它的服務器拿到請求暴氏,然后選擇一個合適的服務器延塑,將請求轉交給它。
因此答渔,兩者的區(qū)別就很明顯了关带,正向代理服務器是幫客戶端做事情,而反向代理服務器是幫其它的服務器做事情沼撕。
比如說現(xiàn)在客戶端的域名為client.com宋雏,服務器的域名為server.com,客戶端向服務器發(fā)送 Ajax 請求务豺,當然會跨域了磨总,那這個時候讓 Nginx 登場了,通過下面這個配置:
Nginx 相當于起了一個跳板機笼沥,這個跳板機的域名也是client.com蚪燕,讓客戶端首先訪問?client.com/api,這當然沒有跨域奔浅,然后 Nginx 服務器作為反向代理馆纳,將請求轉發(fā)給server.com,當響應返回時又將響應給到客戶端汹桦,這就完成整個跨域請求的過程鲁驶。
4)document.domain + iframe跨域
此方案僅限主域相同,子域不同的跨域應用場景营勤。實現(xiàn)原理:兩個頁面都通過js強制設置document.domain為基礎主域灵嫌,就實現(xiàn)了同域壹罚。
5)postMessage跨域
postMessage是HTML5 XMLHttpRequest Level 2中的API葛作,且是為數不多可以跨域操作的window屬性之一,它可用于解決以下方面的問題:
a猖凛、頁面和其打開的新窗口的數據傳遞
b赂蠢、多窗口之間消息傳遞
c、頁面與嵌套的iframe消息傳遞
e辨泳、上面三個場景的跨域數據傳遞
用法:postMessage(data,origin)方法接受兩個參數:
data: html5規(guī)范支持任意基本類型或可復制的對象虱岂,但部分瀏覽器只支持字符串,所以傳參時最好用JSON.stringify()序列化菠红。
origin: 協(xié)議+主機+端口號第岖,也可以設置為"*",表示可以傳遞給任意窗口试溯,如果要指定和當前窗口同源的話設置為"/"蔑滓。