在web瀏覽器中,同源策略 限制了從同一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。javascript
若是兩個頁面的html
三者都相同,則兩個頁面具備相同的源。前端
舉例說明 http://store.example.com/dir/page.html
同源檢測的示例:java
URL | 結果 | 緣由 |
---|---|---|
http://store.example.com/index.html |
成功 | |
http://store.example.com/dir/another.html |
成功 | |
https://store.example.com/index.html |
失敗 | 不一樣協議 ( https和http ) |
http://store.example.com:81/index.html |
失敗 | 不一樣端口 ( 81和80) |
http://news.example.com/index.html |
失敗 | 不一樣域名 ( news和store ) |
http://example.com/index.html |
失敗 | 不一樣域名 (store是一個單獨的自域) |
如下爲4種能夠遇到的跨源node
頁面可能會因某些限制而改變他的源。ios
設置 document.domain
的值,爲其當前域或其當前域的父域。nginx
場景
http://store.example.com/dir/page.html
文檔中的一個腳本執行如下語句document.domain = "company.com"
便可經過同源檢測web
同源策略控制了不一樣源之間的交互。ajax
使用 CORS
容許跨源訪問。express
場景 由瀏覽器發起的跨域 HTTP 請求 (這個你們接觸的最多)
Javascript的APIs中,容許文檔間直接相互引用。可是當兩個文檔的源不一樣時,一些引用方式將對 API對象的訪問添加限制
可使用window.postMessage
場景 使用
<iframe>
嵌套的時候,父子頁面的通訊
存儲在瀏覽器中的數據,如localStorage和IndexedDB,以源進行分割。每一個源都擁有本身單獨的存儲空間,一個源中的Javascript腳本不能
對屬於其它源的數據進行讀寫操做
。
場景 null
MDN的網站給出了這樣的2種解釋:
CORS (Cross-Origin Resource Sharing,跨域資源共享)是一個系統,它由一系列傳輸的HTTP頭組成,這些HTTP頭決定瀏覽器是否阻止前端 JavaScript 代碼獲取跨域請求的響應。 CORS 給了web服務器這樣的權限,即服務器能夠選擇,容許跨域請求訪問到它們的資源。
跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不一樣源服務器上的指定的資源。當一個資源從與該資源自己所在的服務器不一樣的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。
實際上着兩種解釋都同樣。我給出這樣的理解:CORS賦予服務端(一般所說的後端)一個能力,本身控制哪些瀏覽器的請求能夠訪問到它的資源,來解決跨域問題。
附:全部的 CORS
頭
HTTP頭 | 功能 |
---|---|
Access-Control-Allow-Origin |
指示請求的資源能共享給哪些域。 |
Access-Control-Allow-Credentials |
指示當請求的憑證標記爲 true 時,是否響應該請求。 |
Access-Control-Allow-Headers |
用在對預請求的響應中,指示實際的請求中可使用哪些 HTTP 頭。 |
Access-Control-Allow-Methods |
指定對預請求的響應中,哪些 HTTP 方法容許訪問請求的資源。 |
Access-Control-Expose-Headers |
指示哪些 HTTP 頭的名稱能在響應中列出。 |
Access-Control-Max-Age |
指示預請求的結果能被緩存多久。 |
Access-Control-Request-Headers |
用於發起一個預請求,告知服務器正式請求會使用那些 HTTP 頭。 |
Access-Control-Request-Method |
用於發起一個預請求,告知服務器正式請求會使用哪種 HTTP 請求方法。 |
Origin |
指示獲取資源的請求是從什麼域發起的。 |
功能概述 TL;DR
規範要求,對那些可能對服務器數據產生反作用的 HTTP 請求方法(特別是 GET 之外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否容許該跨域請求。服務器確認容許以後,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也能夠通知客戶端,是否須要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。
一句話概述:非簡單請求時,會先發送預檢請求,容許後再發送實際請求
附:node-express框架下,服務端的跨域設置
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Methods', '*');
next();
});
複製代碼
CORS請求默認不發送Cookie和HTTP認證信息,若是想要知道用cookie就要注意3點
- Client端
new XMLHttpRequest()
中withCredentials
設置爲true
- Serive端
HTTP頭
Access-Control-Allow-Credentials
設置爲true
- Serive端
HTTP頭
Access-Control-Allow-Origin
不能設爲星號,必須指定明確的、與請求網頁一致的域名
因此上面的例子要想發送cookie
// service端
`res.header('Access-Control-Allow-Credentials', true);`
`res.header('Access-Control-Allow-Origin', '具體的域名');`
// client端
Jquery `ajax()` `xhrFields: {withCredentials: true}`
Axios `axios.defaults.withCredentials = true`
複製代碼
上面講了簡單請求,但那些纔是簡單請求呢?咱們稱:若不會觸發 CORS 的預檢請求,稱這樣的請求爲「簡單請求」
如下爲簡單請求:
- HTTP Method 組成只能是如下幾種
- GET
- POST
- HEAD
- HTTP Headers 組成
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type 只包含如下類型 (form表單請求)
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
注:只有同時知足以上兩個條件時,纔是簡單請求,不然爲非簡單請求
前面說了這麼多預檢請求,咱們來說一講什麼是預檢請求: 若是咱們在client端發送請求時,例如:
// 原生
var invocation = new XMLHttpRequest();
invocation.setRequestHeader('X-EXAMPLE', 'xixihaha');
invocation.setRequestHeader('Content-Type', 'application/xml');
// axios
axios.defaults.headers['X-EXAMPLE'] = 'xixihaha';
axios.defaults.headers['Content-Type'] = 'application/xml';
複製代碼
POST 請求發送一個 XML 文檔,該請求包含了一個自定義的請求首部字段(X-EXAMPLE: xixihaha)。另外,該請求的 Content-Type 爲 application/xml。所以,該請求須要首先發起「預檢請求」。
server端 的HTTP頭設置
Access-Control-Allow-Origin: '具體的域名'
Access-Control-Allow-Methods: POST, GET, OPTIONS // 可包含的參數
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type // 容許的首部字段
Access-Control-Max-Age: 86400
複製代碼
非簡單請求和簡單請求無異,若是瀏覽器的預檢請求被服務器接受,則發送實際請求,未被接受則拒絕請求。
跨域不止於此
JSONP
動態建立script標籤,而後利用script的src 不受同源策略約束來跨域獲取數據
function addScriptTag() {
var script = document.createElement("script");
script.src = "http://foo.example?callback=handleResponse";
document.body.appendChild(script);
}
function handleResponse() {
console.log('跨域數據');
};
複製代碼
如下跨域方案不作過多解釋
上文提到的
postMessage()
nginx
轉發,即架設服務器代理
window.name