跨域解决方案

跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

 

跨域解决方案:

一、 JSONP跨域

jsonp跨域其实也是JavaScript设计模式中的一种代理模式。在html页面中通过相应的标签从不同域名下加载静态资源文件是被浏览器允许的,所以我们可以动态的创建script标签,再去请求一个带参网址来实现跨域通信。

  1. JS实现

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

       // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
       script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
       document.head.appendChild(script);

       // 回调执行函数
       function handleCallback(res) {
           alert(JSON.stringify(res));
      }
     </script>
  1. jQuery 实现

    $.ajax({
       url: 'http://www.domain2.com:8080/login',
       type: 'get',
       dataType: 'jsonp',  // 请求方式为jsonp
       jsonpCallback: "handleCallback",    // 自定义回调函数名
       data: {}
    });
  1. Vue 实现

    this.$http.jsonp('http://www.domain2.com:8080/login', {
       params: {},
       jsonp: 'handleCallback'
    }).then((res) => {
       console.log(res);
    })

JSONP的优缺点

  • 优点:不会受到同源策略的限制;兼容性更好,在更加古老的浏览器中都可以运行。

  • 缺点:只支持GET请求而不支持POST等其它类型的HTTP请求。

 

二、跨域资源共享(CORS) ——> 主流跨越解决方案

CORS是一个W3C标准,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

实现CORS通信的关键是服务器。只要服务器实现了CORS接口(服务端设置Access-Control-Allow-Origin即可),就可以跨源通信。

  1. 前端设置

    ajax:

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

    jQuery

    $.ajax({
       ...
      xhrFields: {
          withCredentials: true    // 前端设置是否带cookie
      },
      crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie
       ...
    });

    Vue axios

    axios.defaults.withCredentials = true
  1. 服务器设置

    Nodejs后台

    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...');

 

三、nginx代理跨域

  • 跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。

  • 实现思路:通过nginx配置一个代理服务器(域名相同,端口不同)做跳板机,反向代理访问服务器接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

 

四、Nodejs中间件代理跨域

  node中间件实现跨域代理,是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数 修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

 

五、WebSocket 协议跨域

  • WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

  • 原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

 

六、document.domain + iframe 跨域

  这种方式的使用要求是主域名相同,如果主域名不同的话,则无法使用。

  实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域

 

七、 location.hash + iframe跨域

  实现原理: a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相 同域之间直接js访问来通信。

 

八、 window.name + iframe跨域

  实现条件:1.iframe标签的跨域能力

       2.window.names属性值在文档刷新后依然存在的能力

 

九、postMessage跨域

  postMessage是HTML5 中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递

  • 多窗口之间消息传递

  • 页面与嵌套的iframe消息传递

  • 上面三个场景的跨域数据传递

  用法:postMessage(data,origin)方法接受两个参数

     1. data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。

     2. origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

 

参考文章:https://segmentfault.com/a/1190000011145364

posted @ 2019-08-30 19:16  Irelia9102  阅读(148)  评论(0编辑  收藏  举报