跨域解决方案

栏目: 服务器 · 发布时间: 4年前

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

为了降低网站的安全风险,浏览器引入了同源策略,同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,这是一个用于隔离潜在恶意文件的重要安全机制,同源策略必须确保“协议+域名+端口”三者都相同,即使两个不同的域名指向同一个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.');
    });
});
复制代码

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

查看所有标签

猜你喜欢:

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

蚂蚁金服

蚂蚁金服

由曦 / 中信出版集团股份有限公司 / 2017-4-7 / CNY 59.00

在中国,支付宝(其母公司为蚂蚁金服)是一个家喻户晓的品牌。我们在用手机扫码支付,或者用余额宝理财的时候,一定会和支付宝发生关系。但是很多人不知道,支付宝的母公司叫作“蚂蚁金服”。蚂蚁金服不仅有支付宝,还有余额宝、网商银行、芝麻信用等一系列产品和服务。成立于2004年、起始于支付宝的蚂蚁金服集团,如今已经是全球估值最高的科技金融企业。然而,在成立之初,它只是淘宝网的结算部门,员工只有区区几人,记账用......一起来看看 《蚂蚁金服》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具