详解跨域问题产生及解决

38 阅读4分钟

跨域常见出现场景:

ajax请求报错: 运行在 https://a.com 的 JavaScript 代码使用 XMLHttpRequest/fetch api 来发起一个到 https://b.com/data 的请求。 image.png

一、跨域问题为何产生的?

在于浏览器对于页面安全所制定的防护措施,即同源策略。

浏览器的同源策略: 同源策略:默认同一个域之间可以互相访问或操作资源。页面的源与运行中加载的脚本的源不一致时,出于安全考虑,会对跨域的资源访问或操作作出限制。

页面的源与访问的源

源的定义:

源=协议+主机名+端口

源相同,称为同源,不同,称为跨源或跨域参考:mdn-源

资源有哪些?

注:以上资源,每次加载,浏览器会自动发起get请求

如何限制资源的?

  • 对于script及css 的src属性允许跨域。但加载的资源限制了js的权限,不能读/写返回的内容
  • 对于ajax,禁止跨域

出于什么安全考虑?

1.保证用户信息的安全,防止恶意的网站窃取数据。

如浏览器存储着用户数据,比如认证令牌、cookie 及其他私有元数据,若允许跨域访问,a.com中的脚本就可以访问并操纵b.com中的数据,造成数据泄漏。

实操:

测试源不同: 以网易云课堂网站https://study.163.com/category/480000003121055?为例,鼠标向下滚动,点开右侧菜单栏在线客服链接https://qiyukf.com/client,进入第二个页面,在客服页面的控制台输入以下代码进行测试。

image.png

let parent = opener.document //通过opener获取第一个页面。
parent.cookie // 获取cookie信息

image.png 可以发现 ,由于源不同,浏览器报错,且报错信息提示无法访问document。

测试相同源: image.png

image.png

可以看到,相同源可以访问cookie信息。

(注:这里测试的时候发现,如果链接由a标签包裹,opener会变为空。这是因为若a标签添加了_blank属性,高版本浏览器会将opener 置空参考:mdn-a标签-安全与隐私

2.防止被篡改dom,ccsom,保证网站的完整性。

测试代码改为:

let parent = opener.document //通过opener获取第一个页面。
pdom.body.style.display = "none" // 修改第一个页面的dom
  1. 向服务器发送信息,伪造请求。

但某些情况下,若不同源的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&param=name';
document.head.appendChild(script);

// 定义回调函数
function handleCallback(res) {
    //...
    console.log(res)
}

优点: 可与服务器进行双向通信

缺点:

  1. 只能发送get请求
  2. 无法获知请求是否失败,

4.img

原理:img标签的src属性不受同源策略的限制。

缺点:

  1. 只支持get请求
  2. 只能与服务器单向通信,无法获取服务器的数据,只能通过onload和onerror知道什么时候接收到响应。

参考资料: w3c-同源策略 mdn-同源策略