九种跨域解决方案

作者: CRUD_科科 | 来源:发表于2019-03-18 10:47 被阅读4次
何为跨域

  想必大家都知道,不在赘述了,跨域问题出现的原因就是浏览器的安全机制:同协议、域名、端口。下面就总结下常用的几种解决方案。

1、JSONP(只能发送get请求,不支持post、put、delete;不安全xss攻击)

  jsonp的详细介绍在另一篇文章,这里就以百度的查询接口做简单展示:

<script>
// 封装简单的jsonp
function jsonp(url, params, cb) {
  return new Promise((resovle, reject) => {
    let script = document.creatElement('script');
    window[cb] = function(data) {
      resovle(data);
      document.body.removeChild(script);
    }
    params = {...params, cb}; //wd=b&cb=show
    let arrs = [];
    for(let key in params) {
      arrs.push(`${key}=${params[key]}`);
    }
    script.url =  `${url}?${arrs.join('&')}`;
    document.body.appendChild(script);
 })
}
// jsonp调用方式
jsonp({
  url:'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su',
  params:{wd: 'b'},
  cb: show
}).then(data=>{
  console.log(data)
})
</script>
2、cors(后台配置)

  下面以express为例

// 设置那些原可以访问接口
res.setHeader('Access-Control-Allow-Origin', origin) 
// 允许携带哪个头访问
res.setHeader('Access-Control-Allow-Headers', 'name') 
// 允许那些请求方法
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,DELETE') 
// 允许携带cookie
res.setHeader('Access-Control-Allow-Credentials',true) 
// 预检测存活时间(options请求)
res.setHeader('Access-Control-Max-Age',6000) 
// 允许前端获取哪个请求头(允许返回的头)
res.setHeader('Access-Control-Expose-Header','name') 
3、iframe postMessage

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

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的 iframe 消息传递
  • 上面三个场景的跨域数据传递
    postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。

otherWindow.postMessage(message, targetOrigin, [transfer]);

  • message: 将要发送到其他 window 的数据。
  • targetOrigin:通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
  • transfer(可选):是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
    接下来我们看个例子: http://localhost:3000/a.html页面向http://localhost:4000/b.html传递“我爱你”,然后后者传回"我不爱你"。
// a.html
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe//等它加载完触发一个事件
  //内嵌在http://localhost:3000/a.html
    <script>
      function load() {
        let frame = document.getElementById('frame')
        frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据
        window.onmessage = function(e) { //接受返回数据
          console.log(e.data) //我不爱你
        }
      }
    </script>
// b.html
  window.onmessage = function(e) {
    console.log(e.data) //我爱你
    e.source.postMessage('我不爱你', e.origin)
 }
4、window.name

window.name 属性的独特之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
  a、b页面同域,a页面嵌套c页面,onload事件第一次载入c页面url变为b页面并且获取contentWindow.name

 // a.html(http://localhost:3000/b.html)
  <iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
  <script>
    let first = true
    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    function load() {
      if(first){
      // 第1次onload(跨域页)成功后,切换到同域代理页面
        let iframe = document.getElementById('iframe');
        iframe.src = 'http://localhost:3000/b.html';
        first = false;
      }else{
      // 第2次onload(同域b.html页)成功后,读取同域window.name中数据
        console.log(iframe.contentWindow.name);
      }
    }
  </script>
// c.html(http://localhost:4000/c.html)
  <script>
    window.name = '我不爱你'
  </script>
5、hash

  a中嵌套c,c中嵌套b,a=>b=>c传递location.hash,a页面用window.onhashchange获取hash值

// a.html
  <iframe src="http://localhost:4000/c.html#iloveyou"></iframe>
  <script>
    window.onhashchange = function () { //检测hash的变化
      console.log(location.hash);
    }
  </script>
 // b.html
  <script>
    window.parent.parent.location.hash = location.hash
    //b.html将结果放到a.html的hash值中,b.html可通过parent.parent访问a.html页面
  </script>
 // c.html
 console.log(location.hash);
  let iframe = document.createElement('iframe');
  iframe.src = 'http://localhost:3000/b.html#idontloveyou';
  document.body.appendChild(iframe);
6、document.domain

  必须是一级域名和二级域名的关系

// a.html
<body>
 helloa
  <iframe src="http://b.zf1.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf1.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
</body>
// b.html
<body>
   hellob
   <script>
     document.domain = 'zf1.cn'
     var a = 100;
   </script>
</body>
7、websocket

可以参考阮大的文章

// socket.html
<script>
    let socket = new WebSocket('ws://localhost:3000');
    socket.onopen = function () {
      socket.send('我爱你');//向服务器发送数据
    }
    socket.onmessage = function (e) {
      console.log(e.data);//接收服务器返回的数据
    }
</script>
// server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');//记得安装ws
let wss = new WebSocket.Server({port:3000});
wss.on('connection',function(ws) {
  ws.on('message', function (data) {
    console.log(data);
    ws.send('我不爱你')
  });
})
8、ngxin

实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。

使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。

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

先下载nginx,然后将 nginx 目录下的 nginx.conf 修改如下:

// proxy服务器
server {
    listen       80;
    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;
    }
}
9、webpack proxy
//服务器启动目录;
  devServer: {
    contentBase: './dist',
    hot: true,
    // host:'1ocalhost',
    port: 8586,
    // compress:true,

    //解决跨域
    proxy: {
      '/api': {
        target: 'http://localhost:8087',
        pathRewrite: { '^/api': '' },
        changeOrigin: true,
        secure: false, // 接受 运行在 https 上的服务
      }
    }
  },

相关文章

  • Web前后端跨域问题处理

    跨域问题有前台跨域(iframe间)和后台跨域。 前台跨域的解决方案可以采用跨域文档通讯(Cross domain...

  • 跨域问题,解决方案

    跨域问题,解决方案 - Nginx反向代理跨域问题,解决方案 - CORS方案此为原作者的链接:跨域问题,解决之道

  • 前端如何解决常见跨域问题

    跨域解决方案 1、 通过jsonp跨域 2、 document.domain + iframe跨域 3、 loca...

  • 开发环境跨域和生产环境跨域

    开发环境跨域 前后端分离,IP地址不同,请求数据必然导致跨域问题;解决方案:proxy设置 生产环境跨域 解决方案...

  • 浏览器跨域及其解决方案

    title: 浏览器跨域及其解决方案author: Maydate: 20220428 什么是跨域跨域的表现解决跨...

  • 跨域解决方案

    跨域解决方案 跨域解决方案有:设置document.domain,使用带src标签,JSONP,navigatio...

  • 跨域

    参考资料 HTTP访问控制(CORS)跨域解决方案跨域详解

  • Angular访问WebApi出现options方法

    解决方案: 解决跨域:

  • 跨域解决方案(史上最易懂)

    跨域总结 1.跨域思路 跨域解决方案一般分为两种:前端解决,后端解决 1.1 前端解决方案 通过前端解决的思想就是...

  • ZUUL跨域问题

    云环境中每个服务自己有跨域解决方案,而网关需要做最外层的跨域解决方案.如果服务已有跨域配置网关也有,会出现*多次配...

网友评论

    本文标题:九种跨域解决方案

    本文链接:https://www.haomeiwen.com/subject/dgimmqtx.html