1、浏览器的同源策略
同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介
1.1.同源的定义
如果两个 URL 的 protocol、port (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源。这个方案也被称为“协议/主机/端口元组”,或者直接是 “元组”。(“元组” 是指一组项目构成的整体,双重/三重/四重/五重/等的通用形式)
下表给出了与 URLhttps://ims.blibee.com/#/produce/skuProduc的源进行对比的示例:
URL地址 | 是否同源 | 原因 | 备注 |
---|---|---|---|
https://abc.efg.com/#/menu/allMenu | 同源 | 只有路径不同是同源的 | |
http://abc.efg.com/#/menu/allMenu | 不同源 | 协议不同 | |
https://abc.efg.com:8888/#/menu/allMenu | 不同源 | 端口不同 | |
http://xyz.efeaef.com/#/sysConfig | 不同源 | 主机,域名均不同 | |
1.2源的继承和更改
在页面中通过 about:blank
或 javascript:
URL 执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URLs 没有包含源服务器的相关信息。
满足某些限制条件的情况下,页面是可以修改它的源。脚本可以将 document.domain
的值设置为其当前域或其当前域的父域。如果将其设置为其当前域的父域,则这个较短的父域将用于后续源检查。
1.3跨源网络访问
同源策略控制不同源之间的交互,例如在使用XMLHttpRequest
或 <img>
标签时则会受到同源策略的约束。这些交互通常分为三类:
跨域写操作 | 一般是被允许的。例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加 preflight |
跨域资源嵌入 | 一般是被允许(后面会举例说明) |
跨域读操作 | 一般是不被允许的,但常可以通过内嵌资源来巧妙的进行读取访问 |
可嵌入跨域资源的一些示例:
示例类型 | 标签名 | 备注 |
---|---|---|
JavaScript脚本 | <script> | 标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到 |
样式文件 | <link> | 由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的 HTTP 头部 Content-Type |
图片文件 | <img> | 支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,... |
视频文件 | <video> | 可以跨域访问或者播放视频资源 |
音频文件 | <audio> | 可以跨域访问或者播放音频资源 |
插件 | <object> 、 <embed> 和 <applet> |
可以跨域访问插件 |
字体 | @font-face | 一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts) |
框架 | <iframe> | 可以跨域加载iframe载入的任何资源。站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互 |
1.4如何允许和禁止跨域访问
可以使用 CORS 来允许跨源访问。CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。
阻止跨域写操作,只要检测请求中的一个不可推测的标记(CSRF token)即可,这个标记被称为 Cross-Site Request Forgery (CSRF) 标记。你必须使用这个标记来阻止页面的跨站读操作。
阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。
阻止跨站嵌入,需要确保你的资源不能通过以上列出的可嵌入资源格式使用。浏览器可能不会遵守 Content-Type
头部定义的类型
1.5同源策略限制类型
类型 | 场景 | 备注 |
---|---|---|
DOM同源限制 |
浏览器中禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况, 不同域名的 iframe 是限制互相访问的。 |
跨域访问的iframe中的DOM是读不到也无法更改的 |
XMLHttpRequest同源限制 | 浏览器中禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求 | |
存储同源限制 | 浏览器中禁止跨域访问cookie、localStorage、sessionStorage、indexedDB等web存储信息 |
其中cookie的设置与其他存储略有不同,置 cookie 时, 可以使用 |
2、解决跨域的几种方式
解决跨域有以下方法:
序号 | 方法名 | 原理 | 实现方式 | 备注 |
---|---|---|---|---|
1 | 降域 |
同源策略认为域和子域属于不同的域, 比如以下域名: child1.a.com 与 a.com, |
通过设置 document.domain='a.com', 浏览器就会认为它们都是同一个源。 想要实现以上任意两个页面之间的通信, 两个页面必须都设置documen.domain='a.com |
|
2 | JSONP跨域 |
就是动态创建 不受同源策略约束来跨域获取数据 |
SONP 由两部分组成:回调函数 和 数据。 回调函数是用来处理服务器端返回的数据, 回调函数的名字一般是在请求中指定的。 而数据就是我们需要获取的数据,也就是服务器端的数据 |
|
3 | window.name跨域 |
window 对象有个 name 属性,该属性有个特征:即在一个窗口(window)的生命周期内, 窗口载入的所有的页面(不管是相同域的页面还是不同域的页面)都是共享一个 |
每个页面对 并不会因新页面的载入而进行重置 |
只能用于iframe跨域 |
4 | location.hash跨域 | location.hash 方式跨域,是子框架具有修改父框架 src 的 hash 值,通过这个属性进行传递数据 | 更改 跨域iframe中的hash 值,页面不会刷新,但可以读取到参数 | 但是传递的数据的字节数是有限的,受到URL的长度限制,不同浏览器中不同 |
5 | postMessage |
window.postMessage(message,targetOrigin) 方法是 HTML5 新引进的特性, 可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源 |
调用 postMessage 方法的 window 对象是指要接收消息的那一个 window 对象, 该方法的第一个参数 message 为要发送的消息,类型只能为字符串; 第二个参数 targetOrigin 用来限定接收消息的那个 window 对象所在的域,如果不想限定域,可以使用通配符 * |
需要接收消息的 window 对象,可是通过监听自身的 message 事件来获取传过来的消息, 消息内容储存在该事件对象的 data 属性中。 |
6 | WebSocket |
WebSocket是一种通信协议,使用 该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。 |
||
7 | 设置CORS资源共享 |
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出 |
详细过程可以参考阮一峰老师的文章: |
|
8 | 使用服务器代理 |
因为http请求在服务器与服务器之间是不存在同源策略的限制。所以在进行跨域AJAX请求是, 通常进行服务器代理的方式。实际上,大部分公司的业务都是这么做的,最出名的代理服务器就是 大名鼎鼎的Nginx。 在前端开发过程中,因为本地调试的前端域名通常是locahost,直接进行AJAZ请求,一般都会跨域, 前端框架实际上是搭建了本地的Nodejs服务,通过proxy设置,将本地的请求通过服务区转发, 实现了服务器代理的功能 htttp请求通过服务转发到实际请求的地址当中,这样就避免了直接访问服务造成的 |