同源策略及跨域

前言

首先,何爲同源策略? 同源策略是由Netspace提出來的一種安全策略。同源由一個URL的協議、主機、端口定義,若是這三者一致,那麼就是同源。javascript

當不一樣源的時候,哪些操做被容許呢?html

  1. <script>加載Javascript
  2. <link> 加載CSS
  3. <img>加載圖片
  4. <video><audio>加載多媒體
  5. <object><embed><applet>加載插件
  6. <iframe>加載任何東西
  7. 連接、跳轉、表單提交

哪些不容許呢?前端

  1. 跨域文檔之間使用Javascript腳本進行交互,API的訪問有限制。好比iframe與父頁面不一樣源,想要經過Javascript去操做父頁面DOM是不被容許的
  2. 不一樣源之間的XMLHttpRequest不被容許

1.修改源

一個頁面的源能夠修改的,經過document.domain,好比一個頁面a.b.com嵌入iframec.b.com,這時候只須要把兩個頁面的document.domain設置爲b.com便可。java

2.使用代理

跨域是針對前端來的,服務端是沒有跨域這個東西的,因此後臺設置一下,前端訪問時訪問一個同源頁面,而後後臺把請求的數據轉到不一樣源的頁面便可。node

前端web

var xhr = new XMLHttpRequest();
    var url = 'http://localhost:3000/api';    // 向http://localhost:3000/api發出請求,獲取數據
    xhr.open('GET', url);
    xhr.send(null);
    xhr.onreadystatechange = () => {
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {  // 若是請求成功
            text.innerHTML = xhr.response;
        }
    }
複製代碼

同域服務器ajax

// nodeJs express
    var proxy = require('http-proxy-middleware');
    var app = express();
    app.use('/api', proxy({target: 'http://localhost:3001/', changeOrigin: true}));
複製代碼

3.JSONP

由於<script>內嵌資源是容許的,因此能夠把請求的接口放在<script>標籤上,而後附帶咱們的回調函數express

前端json

<script src="http://other.com/ajax.json?callback=myFunction"></script>
複製代碼

跨域服務器api

接口獲取到callback後,把返回的數據做爲參數傳給callback並執行。

// nodeJs express
  app.get('/', function (req, res) {
      var callbackName = req.query.callback;
      res.send(callbackName+"({'message': 'hello world'});");
  })
複製代碼

缺點

  1. 存在安全問題
  2. 只能是GET請求
  3. 調用是異步的

4.Web Messaging

這個是HTML5的一個接口,用到了接口裏面的postMessage方法,主要用於兩個窗口之間交換數據,不能用於與服務器交換數據。

otherWindow.postMessage(message, targetOrigin, [transfer]);
複製代碼

otherWindow是接收方窗口的引用。通常是如下幾種方式:

  1. window.iframe[0]
  2. document.getElementByTagName('iframe')[0].contentWindow
  3. window.open返回的引用
  4. event.source 接收到數據的源頭

message是支持幾乎全部形式的數據,transfer可省略

用法:

// 父頁面
document.getElementByTagName('iframe')[0].contentWindow.postMessage({"age":10},'http://localhost:8080');
// 監聽有沒有數據返回來
window.addEventListener('message',function(e){
  console.log(e)
});
複製代碼
// iframe
window.addEventListener('mesage',function(e){
  if(e.origin !== 'http://localhost:8081'){
    return;
  }
  console.log(e);
  e.souce.postMessage('hello world',e.origin)
})
複製代碼

5.跨域資源共享CORS

CORS須要瀏覽器和服務器同時支持,關鍵是服務器,只要服務器實現了CORS接口就能夠跨域通訊。

CORS將請求分爲兩類:簡單請求和非簡單請求。

非簡單請求的條件:

  1. 請求方式是「PUT」、「DELETE」
  2. Content-type字段是application/json

簡單請求

對於簡單請求,瀏覽器在頭信息裏面加一個origin字段,說明本次請求來自哪一個源(協議+主機+端口),服務器根據這個值,決定是否贊成。若是origin不在指定的源裏面,就會返回一個正常的HTTP迴應,可是裏面不包含Access-Control-Allow-Origin就知道出錯了,從而拋出一個錯誤,被XMLHttpRequestonerror回調函數捕獲。

這種錯誤沒法經過狀態碼判斷,有多是200

若是源在許可範圍內,就會返回響應,並多出幾個信息字段。

Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
複製代碼
  1. Access-Control-Allow-Origin必須字段。若是是*,表示接收任何域名的請求。
  2. Access-Control-Allow-Credentials可選字段。CORS請求默認是不帶cookie和HTTP信息的,若是爲true,代表服務器許可請求中能夠包含cookie 。除了服務器容許請求附帶cookie和HTTP信息,客戶端也必須容許
    var xhr = new XMLHttpRequest();
        xhr.withCredentials = true;
    複製代碼
  3. Access-Control-Expose-Headers: FooBar該字段可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。若是想拿到其餘字段,就必須在Access-Control-Expose-Headers裏面指定。上面的例子指定,getResponseHeader('FooBar')能夠返回FooBar字段的值。

前端

var xhr = new XMLHttpRequest();
    var url = 'http://localhost:3001';    // 請求的3001端口獲取數據
    xhr.open('GET', url);                 // 與3001端口創建一個鏈接
    xhr.send(null);                       // 發送給3001端口數據爲空
    xhr.onreadystatechange = () => {     // 請求狀態改變後調用這個函數
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {  // 若是請求成功
            text.innerHTML = xhr.response;
        }
    }
複製代碼

跨域服務器

// nodeJs express
    var app = express();
    app.get('/', (req, res) => {
        res.set('Access-Control-Allow-Origin', 'http://localhost:3000'); // 設置容許跨域的origin,容許3000端口訪問本端口(3001)
        res.send("Hello world");
    });
複製代碼

非簡單請求

非簡單請求的不一樣之處在於在正式通訊以前會有一次HTTP查詢請求,成爲「預檢」請求(preflight)

前端

var url = 'http://localhost:3001';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
複製代碼

瀏覽器

瀏覽器發現這是一個非簡單請求,就會自動發出一個預檢請求。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
複製代碼

預檢的請求方式是OPTIONS,表示這個請求是用來詢問的。

跨域服務器

服務器收到預檢請求後,檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段之後確認容許跨域請求,而後作出迴應

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
複製代碼

Access-Control-Allow-Methods代表服務器支持的全部跨域請求的方法。

若是預檢請求經過,之後每次的CORS請求就跟簡單請求同樣了。

缺點

非簡單請求,第一次請求會發送兩次請求

6.WebSocket

WebSocket是不受同源限制的,因此跨域什麼的就不存在了。

基本用法:

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) {
  console.log("Connection open ...");
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
}; 
複製代碼
相關文章
相關標籤/搜索