跨域关键知识
同源策略
- 浏览器故意设计的一个功能限制
CORS
- 突破浏览器限制的一个方法
JSOPN
- IE时代的妥协
同源策略(不同源的页面之间,不准互相访问数据)
同源定义
源
-
window.origin 或 location.origin 可以得到当前源
-
源 = 协议 + 域名 + 端口号
-
如果两个 url 的协议、域名、端口号完全一致,那么这两个 url 就是同源的。
-
举例:qq.com、https://www.baidu.… 不同源;baidu.com、https://www.baidu.… 不同源,完全一致才算同源。
同源策略定义
浏览器规定
-
如果 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 空间页面里的 JS 和黑客网页里的 JS
-
发的请求几乎没有区别(referrer 有区别)
-
如果后台开发者没有检查 referer,那么就完全没区别
-
所以,没有同源策略,任何页面都能偷 QQ 空间的数据,甚至支付宝余额!
那检查 referer 不就好了?
-
安全原则:安全链条的强度取决于最弱一环
-
万一这个网站的后端开发工程师是个傻 X 呢
-
所以浏览器应该主动预防这种偷数据的行为
-
总之,浏览器为了用户隐私,设置了严格的同源策略
步骤
hosts
设置本地域名映射
-
让
qq.com映射到127.0.0.1 -
就可以访问
http://qq.com:8888/index.html了 -
让
frank.com映射到127.0.0.1 -
就可以访问
http://frank.com:9999/index.html了
如何设置 hosts
- 需要用管理员权限操作,打开记事本,找到
跨域 AJAX
正常使用 AJAX
- 在
qq.com:8888里运行的 JS 可以访问/friends.json
黑客偷数据
-
在
frank.com:9999里运行的 JS 不能访问! -
浏览器需要
CORS -
提问:黑客的请求发成功了没有?答:成功了,因为
qq.com后台有 log。黑客拿到响应了没有?答:没有,因为浏览器不给数据给它 -
注意:就没有浏览器不限制跨域么?如果不限制,就是浏览器 bug 了,快向浏览器反馈。
解决跨域的方法一: CORS
问题根源
-
浏览器默认不同源之间不能互相访问数据
-
但是
qq.com和frank.com其实都是一个人的网站 -
就是想要两个网站互相访问,浏览器为什么阻止
-
好吧,用
CORS -
浏览器说,如果要共享数据,需要提前声明!
-
哦,那怎么声明呢?
-
浏览器说,
qq.com在响应头里写frank.com可以访问 -
哦,具体语法呢?
-
Access-Control-Allow-Origin:http://foo.example -
浏览器说:都得文档里,去看 MDN 文档嘛
if (path === "/friends.json") {
response.statusCode = 200;
response.setHeader("Content-Type", "text/json;charset=utf-8");
response.setHeader("Access-Control-Allow-Origin", "http://frank.com:9999");
response.write(fs.readFileSync("./public/friends.json"));
response.end();
}
解决跨域的办法二:JSONP(兼容IE)
- JSONP和JSON没有任何关系
步骤
JSON是什么(优缺点)
- JSONP是我们在跨域的时候由于当前浏览器不支持CORS或者因为某些条件不支持CORS,我们必须使用另外一种方式来跨域,于是我们请求一个JS文件,这个JS文件会执行一个回调,回调里面就有我们的数据。回调的名字是随机生成的,一个随机数,然后把这个名字以 callback 的参数传给后台,后台会把这个函数返回给我们并执行。
JSONP的优点
-
一、兼容IE;
-
二、它可以跨域
JSONP的缺点
-
一、由于它是script的标签,所以它读不到AJAX那么精确的状态(不知道状态码,不知道响应头,只知道成功或者失败);
-
二、由于它是script标签,所以它只能发
get请求,不能发post请求。