跨域请求的几种方式(二)

259 阅读4分钟

接着我们上一篇跨域请求的文章说

postMessage

window.postMessage()是HTML5的一个接口,专注实现在不同窗口不同页面的跨域通信。 用法:postMessage(data,origin) 参数:

  • data:html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
  • origin:协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
  1. a.html:(www.dotest1.com/a.html)
<iframe id="iframe" src="http://www.dotest2.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
            name: 'aym'
        };
        // 向dotest2传送跨域数据
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.dom2.com');
    };

    // 接受domain2返回数据
    window.addEventListener('message', function(e) {
        alert('data from dotest2 ' + e.data);
    }, false);
</script>
  1. b.html:(www.dotest2.com/a.html)
<script>
    // 接收domain1的数据
    window.addEventListener('message', function(e) {
        alert('data from dotest1 ---> ' + 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

cors全称是“跨域资源共享”(Cross-origin resource sharing),cors请求有两种请求,简单请求和非简单请求。 只要同时满足以下两大条件,就属于简单请求。

  1. 请求方法是以下三种方法之一
  • HEAD
  • GET
  • POST
  1. HTTP的头信息不超出以下几种字段
  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

普通请求只需要在服务端设置Access-Control-Allow-Origin即可,也就是告诉什么站点是被允许的。前端什么也不用干只需要正常的发送请求就好。

res.writeHead(200, {
    'Access-Control-Allow-Origin': 'http://www.do1.com', // 允许访问的域(协议+域名+端口)
});

但是如果需要带cookie的话,前后端都需要进行设置

前台

  • 原生ajax
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容

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

xhr.open('post', 'http://www.dotest2.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);
    }
};
  • jQueryajax:
$.ajax({
    ...
   xhrFields: {
       withCredentials: true    // 前端设置是否带cookie
   },
   crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie
    ...
});
  • vue框架
Vue.http.options.credentials = true

后台

  • Node
// 跨域后台设置
res.writeHead(200, {
    'Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie
    'Access-Control-Allow-Origin': 'http://www.dotest1.com', // 允许访问的域(协议+域名+端口)
    /* 
     * 此处设置的cookie还是dotest2的而非dotest1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
     * 但只要dotest2中写入一次cookie认证,后面的跨域接口都能从dotest2中获取cookie,从而实现所有的接口都能跨域访问
     */
    'Set-Cookie': 'l=a123456;Path=/;Domain=www.dotest2.com;HttpOnly' 
    // HttpOnly的作用是让js无法读取cookie
});

非简单请求:如果请求方法是PUT、DELETE,或者Content-type的类型为applicetion/json的。使用非简单请求 会发出一次预检测,返回码是204,预检测通过才会发送真正的请求

  • Node
var allowCrossDomain = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:63342');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type,accept,X-Custom-Header');
    res.header('Access-Control-Allow-Credentials', 'true');
    next();
};
  • 前端
var url = 'http://www.domain2.com:8080/login';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send()

上边代码中,HTTP请求方法是PUT,并且发送了一个自定义头信息X-Custom-Header。浏览器发现这是一个非简单请求 ,就会发出一个预检请求,要求服务器确认可以这样请求。

代理

跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。 **实现思路:**通过nginx配置一个代理服务器(域名与dotest1相同,端口不同)做跳板机,反向代理访问dotest2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

1. nginx具体配置

proxy服务器
server {
    listen 81;
    server_name www.domain1.com;

    location / {
        proxy_pass http: //www.domain2.com:8080;  #反向代理
            proxy_cookie_domain www.domain2.com www.domain1.com;#修改cookie里域名
        index index.html index.htm;

        #当用webpack - dev - server等中间件代理接口访问nignx时, 此时无浏览器参与, 故没有同源限制, 下面的跨域配置可不启用
        add_header Access - Control - Allow - Origin http: //www.domain1.com;  #当前端只跨域不带cookie时,可为*
            add_header Access - Control - Allow - Credentials true;
    }
}
  • 前端代码示例
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
  • 后台Nodejs

2. Nodejs中间件代理插件 这篇有详细说明详见

WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。 原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。