前端跨域整理总结
跨域,一个老生常谈的问题,也是前后端交互必定也经常会碰到的问题,相信大家都不陌生,虽然不是什么高深的东西,但是脚手架的层层封装,各种 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上传
- 请求方法是:
HEAD
、GET
、POST
,三者之一 - 请求头信息不超过以下几个字段:
- 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请求会额外发送的头信息字段,用逗号隔开
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 插件
5. postMessage
postMessage是HTML5标准中的API,它可以给我们解决如下问题:
- 页面和新打开的窗口间数据传递
- 多窗口之间数据传递
- 页面与嵌套的 iframe 之间数据传递
- 上面三个场景之间的
跨域传递
- 参数一:发送的数据
- 参数二:你要发送给谁就写谁的地址
(协议 + 域名 +端口
),也可以设置为*
,表示任意窗口,为/
表示与当前窗口同源的窗口
// 发送方
window.parent.pastMessage('发送的数据','http://接收的址')
如果是向 iframe 发送的话
// 发送方 window.parent.pastMessage('发送的数据','http://接收的址')
// 接收方
window.addEventListener('message',(e)=>{
console.log('接收到的数据:' + e.data
})
6. JSONP
【前端跨域整理总结】原理就是通过添加一个
服务器返回并立即执行
handleCallback({ code: 200, msg: 'success', data: [] })
跨域时 Cookie 要做何处理? 指的就是对第三方使用 Cookie 的设置,在 Cookie 信息中添加
SameSite
属性Set-Cookie: widget_session=123456;
SameSite=None;
Secure
SameSite 有三个值:
strict
:严格模式,完全禁止使用Cookielax
:宽松模式,允许部分情况使用Cookie,跨域的都行
,a标签跳转,link标签,GET提交的表单none
:任何情况下都会发送Cookie,但必须同时设置Secure属性,意思是需要安全上下文,Cookie只能通过https发送
,否则无效
不过在最新的
Chrome91
版本中这个已经被移除
了,所以在 91之前的版本依然可以使用如果 Chrome 或 Edge 版本大于91小于94的话,可以通过Chromium支持的command-line flag
- 右键 Chrome 或 Edge 浏览器,选择属性
- 在目标(Target)属性末尾加上
--disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure
并且官方说的到 94 版本会连 comman-line 也会移除
官方的说法是任由开发者控制这两个选项,容易被攻击
结语 我觉得能不跨域的话还是还是尽量保持同域好,开发用下前端的代理,部署用Nginx代理一下。或者在请求中设置跨域头,原理都是一样的,就是响应头设置跨域参数而已
点赞支持、手留余香、与有荣焉
推荐阅读
- 20190302|20190302 复盘翻盘
- 【韩语学习】(韩语随堂笔记整理)
- Jsr303做前端数据校验
- 三国谋略22(找准你的定位)
- 7、前端--jQuery简介、基本选择器、基本筛选器、属性选择器、表单选择器、筛选器方法、节点操作、绑定事件
- 前端代码|前端代码 返回顶部 backToTop
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- iOS|iOS runtime应用整理
- 整理师囍囍的日记|整理师囍囍的日记 day19
- 前端|web前端dya07--ES6高级语法的转化&render&vue与webpack&export