跨域解决方案

为了下降网站的安全风险,浏览器引入了同源策略,同源策略限制了从同一个源加载的文档或脚本如何与来自另外一个源的资源进行交互,这是一个用于隔离潜在恶意文件的重要安全机制,同源策略必须确保“协议+域名+端口”三者都相同,即便两个不一样的域名指向同一个ip地址,也不符合同源策略,不符合同源策略的请求即产生了跨域。但跨域并非浏览器限制了请求,而是拒绝接受,也就是说请求会发送到服务器而后从服务器返回,可是浏览器拒绝接收。javascript

同源策略限制了如下几种行为:css

  • Cookie、LocalStorage和IndexDB没法读取;
  • DOM和JS对象没法得到;
  • AJAX请求不能发送。

处理跨域问题的方案包括:html

JSONP

一般,为了减轻web服务器的压力,咱们会把js、css、img等静态资源分离到另外一台独立域名的服务器上,再在html中经过相应的标签从不一样的域名下加载静态资源,这是被浏览器容许的,JSONP就是利用浏览器的这一漏洞实现跨域。前端

<script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 传参并指定回调执行函数为fn
    script.src = 'http://www.domain.com:8080/login?user=admin&callback=fn';
    document.head.appendChild(script);

    // 回调执行函数
    function fn(res) {
        alert(JSON.stringify(res));
    }
 </script>
复制代码

服务端返回函数的调用并把数据做为参数传递给浏览器:java

fn({'status': true, code: 200, message: '成功'})
复制代码

后台node实例:node

var querystring = require('querystring');
var http = require('http');
var server = http.createServer();

server.on('request', function(req, res) {
    var params = qs.parse(req.url.split('?')[1]);
    var fn = params.callback;

    // jsonp返回设置
    res.writeHead(200, { 'Content-Type': 'text/javascript' });
    res.write(fn + '(' + JSON.stringify(params) + ')');

    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');
复制代码

JSONP的缺点:只能实现get请求。nginx

document.domain + iframe

这个方案仅限于主域相同的状况。web

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var str = 'hello child';
</script>
复制代码
<script>
    document.domain = 'domain.com';
    // 获取父窗口中变量
    alert(window.parent.str);  // hello child
</script>
复制代码

window.postMessage

window.postMessage()是HTML5 XMLHttpRequest Level 2中的API,它能够安全的实现跨域通讯。window.postMessage()方法被调用时,会在全部页面脚本执行以后(在该方法以后设置的事件、以前设置的timeout事件等等)向目标窗口派发一个MessageEvent消息。该MessageEvent消息有四个属性须要注意: message 属性表示该message 的类型; data 属性为 window.postMessage 的第一个参数;origin 属性表示调用window.postMessage() 方法时调用页面的当前状态; source 属性记录调用 window.postMessage() 方法的窗口信息。json

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
  var iframe = document.getElementById('iframe');
  iframe.onload = function() {
    var data = {
      name: 'aym'
    };
    // 向domain2传送跨域数据
    iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
  };

  // 接受domain2返回数据
  window.addEventListener('message', function(e) {
    alert('data from domain2 ---> ' + e.data);
  }, false);
</script>
复制代码
<script>
  // 接收domain1的数据
  window.addEventListener('message', function(e) {
    alert('data from domain1 ---> ' + e.data);

    var data = JSON.parse(e.data);
    if (data) {
      data.number = 16;

      // 处理后再发回domain1
      window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
    }
  }, false);
</script>
复制代码

CORS(跨域资源共享)

普通跨域请求:只服务端设置Access-Control-Allow-Origin便可,前端无须设置,若要带cookie请求:先后端都须要设置。目前主流浏览器都支持CORS(IE8/9须要使用XDomainRequest对象)。后端

  • 前端设置:
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');

xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);
    }
};
复制代码
  • 服务端设置:
var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var postData = '';

    // 数据块接收中
    req.addListener('data', function(chunk) {
        postData += chunk;
    });

    // 数据接收完毕
    req.addListener('end', function() {
        postData = qs.parse(postData);

        // 跨域后台设置
        res.writeHead(200, {
            'Access-Control-Allow-Credentials': 'true',     // 后端容许发送Cookie
            'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 容许访问的域(协议+域名+端口)
            /*
             * 此处设置的cookie仍是domain2的而非domain1,由于后端也不能跨域写cookie(nginx反向代理能够实现),
             * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现全部的接口都能跨域访问
             */
            'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的做用是让js没法读取cookie
        });

        res.write(JSON.stringify(postData));
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');
复制代码

WebSocket

确切的说,WebSocket是一种网络通讯协议,它相对于HTTP的优点在于,客户端和服务端能够实现双向通讯,而HTTP只能是由客户端发起请求,好比一个实时的聊天室,客户端没法知晓服务器是否有新数据,所以只能使用“轮询”的方式,这种方式无疑有巨大的弊端,WebSocket能很好地解决这些问题。 WebSocket最大的特色是服务器能够主动向客户端发送消息,客户端也能够主动向服务器发送消息,是真正的双向平等对话。 它还有以下特色:

  • 创建在 TCP 协议之上,服务器端的实现比较容易。
  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,而且握手阶段采用 HTTP 协议,所以握手时不容易屏蔽,能经过各类 HTTP 代理服务器。
  • 数据格式比较轻量,性能开销小,通讯高效。
  • 能够发送文本,也能够发送二进制数据。
  • 没有同源限制,客户端能够与任意服务器通讯。
  • 协议标识符是ws(若是加密,则为wss),服务器网址就是 URL。

客户端示例:(示例引入了Socket.io)

<div>user input:<input type="text"></div>
<script src="./socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');

// 链接成功处理
socket.on('connect', function() {
    // 监听服务端消息
    socket.on('message', function(msg) {
        console.log('data from server: ---> ' + msg);
    });

    // 监听服务端关闭
    socket.on('disconnect', function() {
        console.log('Server socket has closed.');
    });
});

document.getElementsByTagName('input')[0].onblur = function() {
    socket.send(this.value);
};
</script>
复制代码

服务端示例:

var http = require('http');
var socket = require('socket.io');

// 启http服务
var server = http.createServer(function(req, res) {
    res.writeHead(200, {
        'Content-type': 'text/html'
    });
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

// 监听socket链接
socket.listen(server).on('connection', function(client) {
    // 接收信息
    client.on('message', function(msg) {
        client.send('hello:' + msg);
        console.log('data from client: ---> ' + msg);
    });

    // 断开处理
    client.on('disconnect', function() {
        console.log('Client socket has closed.');
    });
});
复制代码
相关文章
相关标签/搜索