前端面试题让前端飞python

九种跨域解决方案

2019-03-18  本文已影响4人  CRUD_科科
何为跨域

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

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 属性之一,它可用于解决以下方面的问题:

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

// 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 上的服务
      }
    }
  },
上一篇下一篇

猜你喜欢

热点阅读