乐趣区

关于http:前端跨域整理总结

跨域,一个陈词滥调的问题,也是前后端交互必然也常常会碰到的问题,置信大家都不生疏,尽管不是什么浅近的货色,然而脚手架的层层封装,各种 API 的层出不穷,未免有些应接不暇,所以这种状况,就只有本人总结一下了,如果你对它并不是那么相熟,置信对必定会对你有帮忙的

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

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

对于同源策略不理解的能够看我另一篇文章有具体介绍:
吃透浏览器平安(同源限度 /XSS/CSRF/ 中间人攻打)

跨域有哪些计划?

这里只介绍几种开发中用的比拟多的,简直用不到的比方:

  • document.domain + iframe:实用主域名雷同,子域名不同的跨域场景
  • window.name + iframe:利用 name 值最长能够 2M,并用不同页面或不同域名加载后仍然存在的个性
  • location.hash + iframe:实用通过 C 页面来实现 A 页面与 B 页面通信的场景

就不过多开展了

1. CORS

面试爱问这个

CORS 通信过程都是浏览器主动实现,须要浏览器 (都反对) 和服务器都反对,所以要害在 只有服务器反对,就能够跨域通信 ,CORS 申请分两类, 简略申请 非简略申请

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

  • 服务器指定了 Access-Control-Allow-Credentials: true
  • 开发者须在申请中关上 withCredentials 属性: xhr.withCredentials = true
  • Access-Control-Allow-Origin 不要设为星号,指定明确的与申请网页统一的域名,这样就不会把其余域名的 Cookie 上传

简略申请

须要同时满足两个条件,就属于简略申请:

  • 申请办法是:HEADGETPOST,三者之一
  • 申请头信息不超过以下几个字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-Id
    • Content-Type:值为三者之一 application/x-www/form/urlencoded、multipart/form-data、text/plain

须要这些条件是为了兼容表单,因为历史上表单始终能够跨域

浏览器间接收回 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,它的头信息中有几个字段

  • Origin: 示意申请来自哪个域,这个字段是必须的
  • Access-Control-Request-Method:列出 CORS 申请会用到哪些 HTTP 办法,这个字段是必须的
  • Access-Control-Request-Headers:指定 CORS 申请会额定发送的头信息字段,用逗号隔开

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

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

示意预检申请的返回后果能够被缓存多久,在这个工夫范畴内再申请就不须要预检了。不过这个缓存只对齐全一样的 URL 才会失效

2. Nginx 代理跨域

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

server { 
    listen 81; server_name www.domain1.com; 
    location / { 
        proxy_pass http://xxxx1:8080; // 反向代理 
        proxy_cookie_domain www.xxxx1.com www.xxxx2.com; // 批改 cookie 里域名 
        index index.html index.htm; 
        // 当用 webpack-dev-server 等中间件代理接口拜访 nignx 时,此时无浏览器参加,故没有同源限度,上面的跨域配置可不启用 
        add_header Access-Control-Allow-Origin http://www.xxxx2.com; // 以后端只跨域不带 cookie 时,可为 * 
        add_header Access-Control-Allow-Credentials true; 
    } 
}

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,它能够给咱们解决如下问题:

  • 页面和新关上的窗口间数据传递
  • 多窗口之间数据传递
  • 页面与嵌套的 iframe 之间数据传递
  • 下面三个场景之间的 跨域传递

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 的设置,在 Cookie 信息中增加 SameSite 属性

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

SameSite 有三个值:

  • strict:严格模式,齐全禁止应用 Cookie
  • lax:宽松模式,容许局部状况应用 Cookie,跨域的都行,a 标签跳转,link 标签,GET 提交的表单
  • none:任何状况下都会发送 Cookie,但必须同时设置 Secure 属性,意思是须要平安上下文,Cookie 只能通过 https 发送,否则有效

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

不过在最新的 Chrome91 版本中这个 曾经被移除 了,所以在 91 之前的版本仍然能够应用

如果 Chrome 或 Edge 版本大于 91 小于 94 的话,能够通过 Chromium 反对的 command-line flag

  • 右键 Chrome 或 Edge 浏览器,抉择属性
  • 在指标 (Target) 属性开端加上
 --disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure

并且官网说的到 94 版本会连 comman-line 也会移除

官网的说法是任由开发者管制这两个选项,容易被攻打

结语

我感觉能不跨域的话还是还是尽量放弃同域好,开发用下前端的代理,部署用 Nginx 代理一下。或者在申请中设置跨域头,原理都是一样的,就是响应头设置跨域参数而已

点赞反对、手留余香、与有荣焉

退出移动版