跨域
# 同源策略
同源:协议、域名、端口相同
# 同源策略的限制
同源策略限制以下几种行为:
Cookie、LocalStorage 和 IndexDB 无法读取
DOM 和 Js 对象无法获得
AJAX 请求不能发送
# 跨域
当一个请求 url 的协议、域名、端口三者之间任意一个与当前页面 url 不同即为跨域
跨域的解决办法:
通过 jsonp 跨域
跨域资源共享(CORS)
nginx 代理跨域
nodejs 中间件代理跨域
postMessage 跨域
WebSocket 协议跨域
# jsonp 跨域
script 标签的 src,img 标签的 src,link 标签的 href 没有被同源策略所限制
原理:动态创建一个<script>
标签,而 script 标签的 src 属性是没有跨域的限制的。,src 中放入链接和回调方法。
优点:兼容性好
缺点:
只支持 get 请求,不支持 post 请求。
安全性不高
jsonp 在调用失败的时候不会返回各种 HTTP 状态码。
jsonp 安全性防范:
1、防止 callback 参数意外截断 js 代码,特殊字符单引号双引号,换行符均存在风险(方案:作严整的格式检查,或强制约定指定格式)
2、防止 callback 参数恶意添加标签(如 script),造成 XSS 漏洞(方案:过滤特殊字符)
3、防止跨域请求滥用,阻止非法站点恶意调用(方案:refer 白名单匹配,以及 cookieToken 机制来限制)
代码实现:
<button id="btn">点击</button>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script>
$("#btn").click(function() {
var frame = document.createElement("script");
frame.src =
"http://localhost:3000/article-list?name=leo&age=30&callback=func";
$("body").append(frame);
});
function func(res) {
alert(res.message + res.name + "你已经" + res.age + "岁了");
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 跨域资源共享(CORS)
普通跨域请求:只服务端设置 Access-Control-Allow-Origin 即可,前端无须设置
若要带 cookie 请求:前后端都需要设置。
前端设置
ajax 设置:
xhr.withCredentials = true; // 跨域请求是否提供凭据信息(cookie、HTTP认证及客户端SSL证明等)
axios 设置:
axios.defaults.withCredentials = true
服务器端设置:
允许前端带认证 cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名
Access-Control-Allow-Credentials: true
允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
Access-Control-Allow-Origin: http://www.domain1.com
# nginx 代理跨域
跨域原理: 同源策略是浏览器的安全策略,不是 HTTP 协议的一部分。服务器端调用 HTTP 接口只是使用 HTTP 协议,不会执行 JS 脚本,不需要同源策略,也就不存在跨越问题
实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。
- 可在 nginx 的静态资源服务器中加入以下配置
location / {
add_header Access-Control-Allow-Origin *;
}
2
3
# nodejs 中间件代理跨域
原理大致与 nginx 相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置 cookieDomainRewrite 参数修改响应头中 cookie 中域名,实现当前域的 cookie 写入,方便接口登录认证。
非 vue 框架的跨域(2 次跨域):
利用 node + express + http-proxy-middleware 搭建一个 proxy 服务器。
vue 框架的跨域(1 次跨域):
利用 node + webpack + webpack-dev-server 代理接口跨域。在开发环境下,由于 vue 渲染服务和接口代理服务都是 webpack-dev-server 同一个,所以页面与代理接口之间不再跨域,无须设置 headers 跨域信息了。
# postMessage 跨域
postMessage 是 HTML5 中的 API,且是可以跨域操作的 window 属性
作用:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的 iframe 消息传递
- 上面三个场景的跨域数据传递
语法:postMessage(data,origin)
data: html5 规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用 JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
# WebSocket 协议跨域
WebSocket protocol 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是 server push 技术的一种很好的实现。
# document.domain + iframe 跨域
此方案仅限主域相同,子域不同的跨域应用场景。
实现原理:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。