跨域常见出现场景:
ajax请求报错:
运行在 https://a.com
的 JavaScript 代码使用 XMLHttpRequest/fetch api
来发起一个到 https://b.com/data
的请求。
一、跨域问题为何产生的?
在于浏览器对于页面安全所制定的防护措施,即同源策略。
浏览器的同源策略: 同源策略:默认同一个域之间可以互相访问或操作资源。页面的源与运行中加载的脚本的源不一致时,出于安全考虑,会对跨域的资源访问或操作作出限制。
源的定义:
源=协议+主机名+端口
源相同,称为同源,不同,称为跨源或跨域参考:mdn-源
资源有哪些?
- link:href huawei.com/index.css
- script: src huawei.com/index.js
- img: src huawei.com/img.jpg
- ajax: huawei.com/userInfo
注:以上资源,每次加载,浏览器会自动发起get请求
如何限制资源的?
- 对于script及css 的src属性允许跨域。但加载的资源限制了js的权限,不能读/写返回的内容
- 对于ajax,禁止跨域
出于什么安全考虑?
1.保证用户信息的安全,防止恶意的网站窃取数据。
如浏览器存储着用户数据,比如认证令牌、cookie 及其他私有元数据,若允许跨域访问,a.com中的脚本就可以访问并操纵b.com中的数据,造成数据泄漏。
实操:
测试源不同:
以网易云课堂网站https://study.163.com/category/480000003121055?
为例,鼠标向下滚动,点开右侧菜单栏在线客服链接https://qiyukf.com/client
,进入第二个页面,在客服页面的控制台输入以下代码进行测试。
let parent = opener.document //通过opener获取第一个页面。
parent.cookie // 获取cookie信息
可以发现 ,由于源不同,浏览器报错,且报错信息提示无法访问document。
测试相同源:
可以看到,相同源可以访问cookie信息。
(注:这里测试的时候发现,如果链接由a标签包裹,opener会变为空。这是因为若a标签添加了_blank属性,高版本浏览器会将opener 置空参考:mdn-a标签-安全与隐私
2.防止被篡改dom,ccsom,保证网站的完整性。
测试代码改为:
let parent = opener.document //通过opener获取第一个页面。
pdom.body.style.display = "none" // 修改第一个页面的dom
- 向服务器发送信息,伪造请求。
但某些情况下,若不同源的dom 能够进行通信怎么办?如果服务器想要给另一个网站提供资源怎么办?详见下部分
二、如何解决
对于dom通信,浏览器引入了跨文档信息机制window.postMessage
对于ajax请求跨域,常见的解决方案有三种,
- 代理(常用,vue,angular 等框架提供跨域配置)
- CORS(常用)
- jsonp
- img 跨域
1. 代理
由于跨域是浏览器的限制,若将请求发送到同源的服务,由同源的服务去请求,则不会产生跨域问题。 具体做法:ajax请求自己同源的服务(代理),然后通过代理去请求跨域的资源。此时,真正发送请求的是代理服务器。
优点:不依赖后端 生产环境:采用nginx 反向代理
2.CORS跨域资源共享
CORS(Cross-Origin Resource Sharing),通过在服务器端设置响应头来允许跨域访问。 阮一峰-跨域资源共享 CORS 详解
3.jsonp
原理:script标签的src属性不受同源策略的限制,资源加载完成后会被当作js脚本立即执行。
实现:手动创建script标签,src中带上callback函数名,及入参。在html中定义好回调函数。 后端返回的资源,包含执行回调函数。
var script = document.createElement('script');
// 给后端传入回调函数名及参数
script.src = 'http://www.huawei.com:8080/test?callback=handleCallback¶m=name';
document.head.appendChild(script);
// 定义回调函数
function handleCallback(res) {
//...
console.log(res)
}
优点: 可与服务器进行双向通信
缺点:
- 只能发送get请求
- 无法获知请求是否失败,
4.img
原理:img标签的src属性不受同源策略的限制。
缺点:
- 只支持get请求
- 只能与服务器单向通信,无法获取服务器的数据,只能通过onload和onerror知道什么时候接收到响应。