同源策略及跨域

前言

首先,何为同源策略? 同源策略是由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.");
}; 
复制代码
相关文章
相关标签/搜索