跨域
跨域关键知识:
- 同源策略。浏览器故意设计的一个功能限制
- CORS。突破浏览器限制的一个方法
- JSONP。IE 时代的妥协
同源
源 = 协议 + 域名 + 端口号
window.origin
或 location.origin
可以得到当前源。
如果两个 url 的协议、域名、端口号完全一致,那么这两个 url 就是同源。
例:https://qq.com
和 https://www.baidu.com
不同源
https://baidu.com
和 https:www.baidu.com
同源
完全一致才算同源
下表给出了与 URL http://store.company.com/dir/page.html
的源进行对比的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html |
同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html |
同源 | 只有路径不同 |
https://store.company.com/secure.html |
失败 | 协议不同 |
http://store.company.com:81/dir/etc.html |
失败 | 端口不同 ( http:// 默认端口是80) |
http://news.company.com/dir/other.html |
失败 | 主机不同 |
同源策略
Ajax 最大的限制是同源策略(Same-origin policy),它限制了不同源之间的交互,一个源的文档或脚本不能与另一个源的资源进行交互。浏览器的同源策略 MDN
浏览器规定
如果 JS 运行在源 A 里,那么就只能获取源 A 的数据
不能获取源 B 的数据,即==不允许跨域==
例如(省略 http 协议)
假设 frank.com/index.html 引用了 cdn.com/1.js
那么就说 1.js 运行在源 frank.com 里
注意 这跟 cdn.com 没有关系,虽然 1.js 从它哪下载
所以 1.js 就只能获取 frank.com 的数据
不能获取 1.frank.com 或者 qq.com 的数据
这是浏览器的功能
浏览器故意要这样设计的
目的:==保护用户隐私==
如果没有同源策略
以 qq 空间为例
源为 https://user.qzone.qq.com
,假设,当前用户已登录(cookie),假设 AJAX 请求 /friends.json 可获取到用户好友列表。
黑客来了,假设有人给你分享 https://qzone-qq.com
给你,实际上是个钓鱼网站,你点开后,这个网页会请求你的好友列表 https://user.qzone.qq.com/friends.json
。这样好友列表就能被黑客访问到。
问题根源:
无法区分发送者
qq 空间页面的 JS 和黑客网页里的 JS 发送的请求几乎没有区别(referrer 有区别)
如果后台开发者没有检查 referrer,那么就完全没有区别
所以,没有同源策略,任何页面都能偷 qq 空间的数据
那检查 referrer 不就好了
安全原则:安全链条的强度取决于最弱的一环
万一这个网站的后端开发工程师就是没有检查 referrer
所以浏览器应该主动预防这种偷数据的行为
总之,浏览器为了用户隐私,设置了严格的同源策略
演示
创建目录
qq-com 里新建 server.js,用来模拟 qq空间
frank-com 里新建 server.js,用来模拟黑客网站
qq-com
public 目录下新建 index.html 首页
qq.js 是 JS 脚本文件
friends.json 是模拟的好友数据
端口监听为 8888,访问
http://127.0.0.1:8888
hacker-com
public 目录下新建 index.html 首页
frank.js 是 JS 脚本文件
端口监听为 9999,范问
http://127.0.0.1:9999
跨域 AJAX
正常使用 AJAX
在 qq.com:8888 里运行的 JS 可以访问 /friends.json
能够访问 黑客偷数据
在 hacker.com:9999 里运行的 JS不能访问
浏览器需要==CORS==
提问
黑客的请求成功了没:
答:成功了,因为 qq.com 后台有 log。
黑客拿到响应了没与?
答:没有,因为浏览器不给数据。
如何跨域
CORS
CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-origin resource sharing)。
问题根源
浏览器默认不同源之间不能互相访问数据
但是 qq.com 和 hacker.com 都是自己的网站,需要互相访问
用 CORS
浏览器说,如果要共享数据,需要提前声明!
qq.com 在响应头里写 hacker.com 可以访问
语法:
Access-Control-Allow-Origin: http://hacker.com:9999
CORS 不兼容 ie 6789
JSONP
什么是 JSONP?
跨域时,由于当前浏览器不支持 CORS 或因为某些条件不支持 CORS,我们必须使用另外一种方式来跨域。
于是,请求一个 JS 文件,这个 JS 文件会执行事先定义好的回调,这个回调里就有我们需要的数据。
优点:
- 支持 IE
缺点:
- 由于是 script 标签,获取不到响应状态
- 不支持 POST
演示:hacker.com 访问 qq.com
- qq.com 将数据写到 /friends.js
- frank.com 用 script 标签引用 /friends.js
- hacker.com 执行 事先定义好的 window.xxx 函数
- /friends.js 执行 window.xxx({friend: […]})
- 然后 hacker.com 就通过 window.xxx 获取到数据了
- window.xxx 就是一个回调!!
JSONP 的实现原理演示:
hacker-com 里的 hacker.js
1 | window.xxx = (data) => { |
qq-com public目录下新建 friends.js 内容为 window.xxx ( {{ data }} )
qq-com 的 server.js 添加如下路由
1 | else if (path === '/friends.js') { |
优化:
window.xxx 能不能改其他名字?
其实名字不重要,只要 hacker.com 定义的函数名和 qq.com/friends.js 执行的函数名是同一个即可。
将名字穿给 /friends.js
1 | // hacker.js |
1 | // server.js |
1 | // friends.js |
进一步优化
1 | // hacker.js |
1 | // server.js |
JSONP 的本质是前后端的协作,即前端把想要的资源以及后续的处理都告诉后台,后台封装好返回给前端执行。
优质博客: