跨域问题
浏览器的同源策略
首先了解下浏览器的同源策略 同源策略
SOP(Same origin policy)
是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS
、CSFR
等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip
地址,也非同源
存在跨域问题的场景:
- 资源跳转: A链接、重定向、表单提交
- 资源嵌入:
<link>、<script>、<img>、<iframe>
等dom标签,还有样式中background:url()、@font-face()
等文件外链 - 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
同源策略限制以下几种行为: 1.) Cookie、LocalStorage 和 IndexDB 无法读取 2.) DOM 和 Js对象无法获得 3.) AJAX 请求不能发送
请求跨域
- jsonp跨域
原理:利用script标签加载完成后会自动执行的特性,后端返回携带数据的代码,触发执行前端已有的函数
缺点:只能实现get请求
js
// 动态创建一个script
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'; // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
js
// 服务端返回
handleCallback({"status": true, "user": "admin"})
- 跨域资源共享(CORS)
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
cors规定了三种不同的交互模式,三种模式的影响越来越大,要求也越来越严格
- 简单请求
- 条件
- get post head
- 没有额外的自定义字段
- content-type: text/plain或multipart/form-data/application/x-www-form-urlencoded
- 规范
- 请求头中自动添加
Origin
- 服务器响应头包含
Access-Control-Allow-Origin
- 请求头中自动添加
- 条件
- 需要预检的请求
- 浏览器发送预检请求(不会实际显示出来),询问服务器是否允许
- OPTIONS
- 如果允许,再发送实际请求
- 浏览器发送预检请求(不会实际显示出来),询问服务器是否允许
- 附带身份凭证的请求: 默认情况下跨域不会附带cookie
- 如果要携带
- xhr:
withCredentials: true
- fetch:
credentials: 'include'
- xhr:
- 会发送预检请求
- 允许服务器设置
Access-Control-Allow-Credentials: true
- 这种模式下,服务器不得设置
Access-Control-Allow-Origin
为*
- 允许服务器设置
- 如果要携带
补充 在跨域时js只能拿到一些基础的响应头,Access-Control-Expose-Headers
设置允许浏览器访问的头的白名单`
- 代理跨域
- 通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
- 开发时采用的proxy也是代理跨域
- WebSocket协议跨域
WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
跨标签页通信
多个标签页之间的通信https://kaifeiji.cc/post/interactions-between-windows-postmessage-and-broadcastchannel/
Broadcast Channel API
: 广播- postMessage: 一般用于定向传递数据 用法:postMessage(data,origin)方法接受两个参数
- data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
- origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
- service worker
sharedWorker
localStorage
: window.onstorage事件监听- cookie\indexdb轮询
- websocket: 需要服务器中转
其它(了解)
- document.domain + iframe
此方案仅限主域相同,子域不同的跨域应用场景
原理:页面和页面内iframe都通过js强制设置document.domain为基础主域,就实现了同域
前端
- 原生 ajax : axios.defaults.withCredentials = true
- axios:axios.defaults.withCredentials = true
服务端
- javascript
'Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie 'Access-Control-Allow-Origin': 'http://www.domain1.com', // 允许访问的域(协议+域名+端口)
nginx 代理跨域
#proxy服务器
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}