跨域

83 阅读1分钟

背景

同源策略

同源策略是一种约定 是浏览器最核心也是最基本的安全功能

同源: “协议 域名 端口” 三者要保持相同

限制内容

本地的存储内容 如Cookie LocalStorage等等

三种标签允许跨越

<img src="">

<link href="">

<script src="">

解决方案

JSONP

基于script标签 不会受到影响 实现相当简单 兼容性好

缺点:

仅支持get方式请求

可能遭受XSS

function jsonp( url, params, callback ) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[callback] = function(data) {
      /* 服务器通过callback来返回data */
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
​
jsonp({
  url: 'http://localhost:3000/say',
  params: { wd: 'Iloveyou' },
  callback: 'show'
}).then(data => {
  console.log(data)
})
​
​
/* 服务器 */
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
  let { wd, callback } = req.query
  res.end(`${callback}('data')`)
})
app.listen(3000)

CORS

需要后端服务器实现CORS

简单请求

条件1:使用下列方法之一:

  • GET
  • HEAD
  • POST

条件2:Content-Type 的值仅限于下列三者之一:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

服务器端设置Access-Control-Allow-Origin 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。浏览器请求时需要加入origin字段 服务器进行匹配决定是否同意此次请求

复杂请求

请求时会先进行 options 的请求(预检请求)

//在请求前进行拦截 设置响应报文头信息
app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*')
  next()
})

PostMessage

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

message: 将要发送到其他 window的数据。

targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。

transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权

/* a 向 b请求*/
// 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('data', 'http://localhost:4000') //发送数据
        window.onmessage = function(e) { //接受返回数据
          console.log(e.data) //test
        }
      }
    </script>
​
// b.html
  window.onmessage = function(e) {
    console.log(e.data) //test
    e.source.postMessage('test', e.origin)
 }
​

WebSocket(H5)

// 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('我不爱你')
  });
})

Node中间件代理

基于服务器之前请求无需遵循同源策略

中间件代理服务器需要和浏览器实现CORS

nginx反向代理

最简单的跨域方式 只需要修改nginx的配置

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

window.name + iframe

通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

location.hash + iframe

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

document.domain + iframe

该方式只能用于二级域名相同的情况