同源策略
当”协议+域名+端口”三者相同时,才能够相互访问资源。保证用户信息的安全,防止恶意的网站窃取数据。
限制
- 当前域下的
js
脚本不能够访问其他域下的cookie
、localStorage
和indexDB
等本地存储 - 当前域下的
js
脚本不能够操作访问操作其他域下的DOM
。 - 当前域下
ajax
无法发送跨域请求。
跨域访问
CORS
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器让运行在一个 origin (domain)上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
简单请求
简单请求不会触发 CORS 预检请求。需要满足以下条件才能成为简单请求
请求方法
- HEAD
- GET
- POST
HTTP 的头信息
不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain
请求过程
- 浏览器会直接发出 CORS 请求,它会在请求的头信息中增加一个 Orign 字段,该字段用来说明本次请求来自哪个源(协议+端口+域名)
- 服务器会根据 origin 值来决定是否同意这次请求
1 | Access-Control-Allow-Origin: http://api.baidu.com // 和 Orign 一致,必须要包含,可以为 * |
非简单请求过程
在正式通信之前进行一次 HTTP 查询请求,称为预检请求
请求过程
- 使用
OPTIONS
方法向服务端发起预检请求
1 | Access-Control-Request-Method:该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法。 |
- 服务器在收到浏览器的预检请求之后,会根据头信息的三个字段来进行判断是否允许跨域
1 | Access-Control-Allow-Origin: http://api.baidu.com // 允许跨域的源地址(必须) |
Cookie 相关问题
- 默认情况下在跨域请求,浏览器是不带 cookie 的。但是我们可以通过设置 withCredentials 来进行传递 cookie.
- Access-Control-Allow-Credentials 设置为 true
- Access-Control-Allow-Origin 设置为非
*
JSONP
利用<script>
标签没有跨域限制,通过<script>
标签 src 属性,发送带有 callback 参数的 GET 请求,服务端将接口返回数据拼凑到 callback 函数中,返回给浏览器,浏览器解析执行,从而前端拿到 callback 函数返回的数据。
1 | let count = 0 |
通过 script
标签的形式获取到的数据会被自动执行,所以通过返回回调函数调用的方式自动执行代码
缺点
- 具有局限性, 仅支持 get 方法
- 不安全,可能会遭受 XSS 攻击
postMessage
特点
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的 iframe 消息传递
- 上面三个场景的跨域数据传递
服务代理
服务端转发请求
document.domain + iframe
此方案仅限主域相同,子域不同的跨域应用场景。实现原理:两个页面都通过 js 强制设置 document.domain 为基础主域,就实现了同域。
location.hash + iframe
a 欲与 b 跨域相互通信,通过中间页 c 来实现。 三个页面,不同域之间利用 iframe 的 location.hash 传值,相同域之间直接 js 访问来通信。
说明
- A 与 B 不同域,A 向 B 传递消息时通过修改 B 页面的 hash 值,B 监听 hash 值的变化获取数据
- A 与 C 同域,B 与 A 通信时通过修改 C 页面的 hash 值,C 通过各种手段通知 A hash 值的辩护
window.name + iframe
window.name 属性的独特之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
WebSocket
通过 WebSocket
服务端中转通信