跨域解决方案

栏目: 服务器 · 发布时间: 2个月前

来源: juejin.im

本文转载自:https://juejin.im/post/5cdbc0fb6fb9a032453bd18b,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有。

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

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

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

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

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>
复制代码

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

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

后台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请求。

document.domain + iframe

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

<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() 方法的窗口信息。

<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.');
    });
});
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

为你推荐:

相关软件推荐:

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

跨越

跨越

Lydia / 人民文学出版社 / 2018-4-1 / 39

三跨青年Lydia的一线奋斗笔记,抛却艰深理论,用亲身经验为你打通任督二脉。 揭开思维认知盲区,剖析成长潜在技巧,探知进阶背后逻辑,在拐点到来的时刻,推动人生加速上行。 10大职场潜在成长技巧,13种打破思维认知的的碎片重建,15种正确面对情感的能量释放, 38篇有世界 观,有方法论的故事,为你打开上升通道。 停留在思维层面的改变人生,其实已然陷入困境, 人生上行的实......一起来看看 《跨越》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具