同源政策与跨域问题
什么是浏览器的同源政策?
维基百科对同源政策的定义。简单来说,为了保护用户的数据安全,浏览器规定,对于不同源的网页,它们之间以下的资源交互行为是被禁止的:
- 持久化数据无法获取,包括
Cookie
,LocakStorage
,IndexDB
DOM
无法获取AJAX
请求不能发送
仔细想想,以上 3 种行为对于用户安全来说,都是非常危险的。持久化数据通常含有用户的身份信息、登录态信息;至于 DOM
和 AJAX
这两者的权限都可能导致网页被植入恶意代码。
同源的严格定义
如果说两个网页同源,是指以下 3 个部分都相同:
- 协议
- 主机
- 端口
举例来说,http://www.example.com/dir/page.html
这个网址,协议是 http://
,域名是 www.example.com
,端口是 80
(默认端口可以省略)。它的同源情况如下。
- http://www.example.com/dir2/other.html:同源
- http://example.com/dir/other.html:不同源(域名不同)
- http://v2.www.example.com/dir/other.html:不同源(域名不同)
- http://www.example.com:81/dir/other.html:不同源(端口不同)
不受同源政策限制的资源交互行为
需要明确的一点是,同源政策对于 HTML
标签是没有限制的,这使得网页可以从远程服务器获取资源文件,主要有以下标签:
script
标签用来获取远程脚本文件,JSONP 跨域方式就是利用了这一点link
标签用来获取远程 CSS 文件image
标签用来获取远程图片文件audio
标签用来获取远程音频文件video
标签用来获取远程视频文件
如何规避同源政策来实现数据获取?
在实际开发中,需要突破同源政策的限制来获取数据,主要是在使用 AJAX
向非同源服务器发送请求的场合。主要使用下面的几种方式来规避同源政策获取数据:
- 跨域资源共享
- JSONP
- 服务器代理转发
- 跨文档通信 Cross-document messaging。这是 HTML5 为了解决跨文档通信引入的新 API:
window.postMessage
,但是这种方式能获取的内容是作了严格限制的,原因仍然是出于安全考虑。 - WebSocket
跨域资源共享
即 Cross-Origin Resource Sharing。这是一个很大的话题,而且基本由后端实现 CORS 的支持,详情见参考链接 3 和 4,两篇文章讲的非常清晰。
JSONP
JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个<script>
元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
首先,网页动态插入<script>
元素,由它向跨源网址发出请求。
1 | function addScriptTag(src) { |
上面代码通过动态添加<script>
元素,向服务器example.com
发出请求。注意,该请求的查询字符串有一个callback
参数,用来指定回调函数的名字,这对于 JSONP 是必需的。
服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。
1 | foo({ |
由于<script>
元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo
函数,该函数就会立即调用。作为参数的 JSON 数据被视为 JavaScript 对象,而不是字符串,因此避免了使用JSON.parse
的步骤。
此外, JSONP 的方式仅限于 GET
方法的网络请求。
架设代理服务器,转发请求
架设一个同源的服务器,浏览器向该同源服务器发送 AJAX
请求,再由该服务器转发请求到外部非同源服务器。