前后端分离:跨域解决方案

118 阅读4分钟

同源策略

同源

window.originlocation.origin 可以得到当前源

源 = 协议 + 域名 + 端口号

同源定义

如果两个url的协议、域名、端口号完全一致,那么这两个url就是同源的

举例——

qq.com、http://www.baidu.… 不同源

baidu.com、 http://www,baidu.com 不同源

必须完全一致才同源

同源策略

浏览器规定

如果JS运行在源A里,那么只能获取源A的数据,不能获取源B的数据,即不允许跨域

举例(省略http协议)

假设 river.com/index.html 引用了 cdn.com/1.js,那么就说"1.js运行在源 river.com 里"

注意这跟 cdn.com 没有关系,虽然1.js从它那下载

所以1.js就只能获取 river.com 的数据,不能获取 1.river.com 或者 qq.com 的数据

这是浏览器的功能

为了保护用户隐私,浏览器故意这样设计的

如果没有同源策略

以QQ空间为例

源为 user.qzone.qq.com

假设当前用户已经登录(用Cookie),假设AJAX请求 /friends.json可获取用户好友列表

黑客攻击

如果有人分享了 qzone-qq.com 的钓鱼网站

点开这个网页,这个网页请求用户好友列表

user.qzone.qq.com/friends.jso…

好友列表就被黑客拿到了

问题的根源
无法区分发送者

QQ空间页面里的JS和黑客网页里的JS,发送请求几乎没有区别(referer有区别)

如果后台开发者没有检查referer,那么就完全没区别

所以,没有同源策略,任何页面都能偷QQ空间的数据

检查referer?

安全原则:安全链条的强度取决于最弱一环

所以浏览器应该主动预防这种行为,总之浏览器为了用户隐私,设置了严格的同源策略

举个例子

源码

步骤
创建目录

qq.com里面有一个server.js,用来模拟QQ空间

baidu.com里面有一个server.js,用来模拟黑客网站

qq.com

/index.html是首页

/qq.js是JS脚本文件

/friends.json是模拟的好友数据

端口监听为8888,访问http://127.0.0.1:8888

baidu.com

/index.html是首页

/frank.js是JS脚本文件

端口监听为9999,访问http://127.0.0.1:9999

设置本地域名映射

qq.com映射到127.0.0.1,直接访问qq.com:8888/index.html

baidu.com映射到127.0.0.1,直接访问baidu.com:9999/index.html

AJAX
正常使用AJAX

qq.com:8888里运行的JS可以访问/friends.json

黑客偷数据

baidu.com:9999里运行的JS不能访问

浏览器需要CORS

提问

黑客的请求发送成功了,qq.com后台有log。但是黑客没有拿到响应,因为浏览器不给数据。

其他疑问

  1. 为什么a.qq.com访问qq.com也算跨域?

    因为历史上,出现过不同公司共同域名,a.qq.comqq.com不一定是同一个网站,浏览器谨慎起见,认为这是不同的源。

  2. 为什么不同端口也算跨域?

    原因同上,一个端口一个公司。

  3. 为什么两个网站的IP一样,也算跨域?

    原因同上,IP可以共用。

  4. 为什么可以跨域使用CSS、JS和图片?

    同源策略限制的是数据访问,我们引用CSS、JS和图片的时候,其实并不知道其内容,只是在引用。

CORS

实现跨域的方法

问题根源

浏览器默认不同源之间不能互相访问数据。

如果qq.combaidu.com其实都是我的网站,我想要两个网站互相访问,浏览器也提供了共享数据的方法。

用CORS

需要提前声明,qq.com在响应头里写baidu.com可以访问。

Access-Control-Allow-Origin

image-20210521233612254.png

JSONP

IE 6 7 8 9 都不支持CORS,若要兼容IE,可以使用JSONP。

定义

JSONPJSON没有关系

步骤

qq.com将数据写到/friends.js

baidu.com用script标签引用/friends.js

/friends.js执行

baidu.com事先定义好window.xxx函数

/friends.js执行window.xxx({friends:[...]})

然后frank.com通过window.xxx获取到数据了

优化

  1. window.xxx可以改其他名字,只要baidu.com定义的函数名和qq.com/friends.js执行的函数名是同一个即可

  2. 封装

    封装成jsonp('url').then(f1,f2)

JSONP是什么?

在跨域的时候,当前浏览器不支持CORS,就可以采用JSONP。

具体操作就是创建一个script请求JS文件,这个JS文件执行回调,回调里面就有需要的数据。

JSONP优点可以支持跨域,缺点由于是script标签,所以做不到AJAX那么精确的状态,只能发get请求,不支持post。