前端跨域整理总结

317次阅读  |  发布于3年以前

跨域,一个老生常谈的问题,也是前后端交互必定也经常会碰到的问题,相信大家都不陌生,虽然不是什么高深的东西,但是脚手架的层层封装,各种 API 的层出不穷,未免有些应接不暇,所以这种情况,就只有自己总结一下了,如果你对它并不是那么熟悉,相信对肯定会对你有帮助的

说跨域之前,首先需要了解的一个概念是同源策略

因为浏览器有安全策略限制,不同源的地址之间不能互相访问资源或者操作DOM

关于同源策略不了解的可以看我另一篇文章有详细介绍:吃透浏览器安全(同源限制/XSS/CSRF/中间人攻击)

跨域有哪些方案?

这里只介绍几种开发中用的比较多的,几乎用不到的比如:

就不过多展开了

1. CORS

整个通信过程都是浏览器自动完成,需要浏览器(都支持)和服务器都支持,所以关键在只要服务器支持,就可以跨域通信,CORS请求分两类,简单请求非简单请求

另外CORS请求默认不包含Cookie以及HTTP认证信息,如果需要包含Cookie,需要满足几个条件:

简单请求

需要同时满足两个条件,就属于简单请求:

需要这些条件是为了兼容表单,因为历史上表单一直可以跨域

浏览器直接发出CORS请求,具体来说就是在头信息中增加Origin字段,表示请求来源来自哪个域(协议+域名+端口),服务器根据这个值决定是否同意请求。如果同意,返回的响应会多出以下响应头信息

Access-Control-Allow-Origin: http://juejin.com // 和 Orign 一致 这个字段是必须的
Access-Control-Allow-Credentials: true // 表示是否允许发送 Cookie 这个字段是可选的
Access-Control-Expose-Headers: FooBar // 指定返回其他字段的值   这个字段是可选的
Content-Type: text/html; charset=utf-8 // 表示文档类型

在简单请求中服务器至少需要设置:Access-Control-Allow-Origin 字段

非简单请求

比如 PUT 或 DELETE 请求,或 Content-Type 为 application/json ,就是非简单请求。

非简单 CORS 请求,正式请求前会发一次 OPTIONS 类型的查询请求,称为预检请求,询问服务器是否支持网页所在域名的请求,以及可以使用哪些头信息字段。只有收到肯定的答复,才会发起正式XMLHttpRequest请求,否则报错

预检请求的方法是OPTIONS,它的头信息中有几个字段

OPTIONS请求次数过多也会损耗性能,所以要尽量减少OPTIONS请求,可以让服务器在请求返回头部添加

Access-Control-Max-Age: Number // 数字 单位是秒

表示预检请求的返回结果可以被缓存多久,在这个时间范围内再请求就不需要预检了。不过这个缓存只对完全一样的URL才会生效

2. Nginx代理跨域

配置一个代理服务器向服务器请求,再将数据返回给客户端,实质和CORS跨域原理一样,需要配置请求响应头Access-Control-Allow-Origin等字段,正向代理和反向代理看我另一篇http的文章有介绍

3. Node中间件代理跨域

在 Vue 中 vue.config.js 中配置

module.export = {
   ...
   devServer: {
       proxy: {
          [ process.env.VUE_APP_BASE_API ]: {
               target: 'http://xxxx',//代理跨域目标接口
               ws: true,
               changeOrigin: true,
               pathRewrite: {
                  [ '^' + process.env.VUE_APP_BASE_API ] : ''
              }
          }
      }
  }
}

Node + express

const express = require('express')
const proxy = require('http-proxy-middleware')
const app = express()
app.use('/', proxy({
   // 代理跨域目标接口
   target: 'http://xxxx:8080',
   changeOrigin: true,
   // 修改响应头信息,实现跨域并允许带cookie
   onProxyRes: function(proxyRes, req, res) {
       res.header('Access-Control-Allow-Origin', 'http://xxxx')
       res.header('Access-Control-Allow-Credentials', 'true')
  },
   // 修改响应信息中的cookie域名
   cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}));
app.listen(3000);

4. WebSocket

WebSocket是HTML5标准中的一种通信协议,以ws://(非加密)和wss://(加密)作为协议前缀,该协议不实行同源政策,只要服务器支持就行

因为WebSocket请求头信息中有Origin字段,表示请求源来自哪个域,服务器可以根据这个字段判断是否允许本次通信,如果在白名单内,就可以通信

// 使用 socket.io 插件
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<script>
   const socket = io('http://xxxx:8080'); // 连接成功处理
   socket.on('connect', function() {
       // 监听服务端消息
       socket.on('message', function(msg) {
           console.log('新消息' + msg);
      });
       // 监听服务端关闭
       socket.on('disconnect', function() {
           console.log('连接关闭');
      })
  })
</script>

5. postMessage

postMessage是HTML5标准中的API,它可以给我们解决如下问题:

postMessage 接受两个参数,用法如下:

// 发送方
window.parent.pastMessage('发送的数据','http://接收的址')

如果是向 iframe 发送的话

// 发送方
<iframe id="iframe" src="http://xxxx"></iframe>
<script>
   const iframe = document.getElementById('iframe')
   iframe.onload = () => {
       iframe.contentWindow.pastMessage('发送的数据','http://接收的址')
  }
</script>
window.parent.pastMessage('发送的数据','http://接收的址')
// 接收方
window.addEventListener('message',(e)=>{
   console.log('接收到的数据:' + e.data
})

6. JSONP

原理就是通过添加一个script标签,向服务器请求JSON数据,这样不受同源政策限制。服务器收到请求后,将数据放在一个callback回调函数中传回来。比如axios。

不过只支持GET请求不安全可能遇到XSS攻击,不过它的好处是可以向老浏览器或不支持CORS的网站请求数据

<script>
   let script = document.createElement('script')
   script.type = 'text/javascript'
   script.src = 'http://juejin.com/xxx?callback=handleCallback'
   document.body.appendChild(script)

   function handleCallback(res){
       console.log(res)
  }
</script>

服务器返回并立即执行

handleCallback({ code: 200, msg: 'success', data: [] })

跨域时 Cookie 要做何处理?

指的就是在 Cookie 信息中添加 SameSite 属性

Set-Cookie: widget_session=123456; SameSite=None; Secure

SameSite 有三个值:

Chrome 80之前默认值是none,之后是lax

参考

https://juejin.cn/post/6916157109906341902

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8