前言
本文所使用的完整代码.可在github仓库获得
博主的个人博客,欢迎来玩
1. 同源策略
1.1 起源
谈到跨域问题就不得不先谈谈同源策略。
同源策略(Same Origin Policy) 是 Netscape 提出的目前所有浏览器都在实行的一个安全政策。同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互,是一种用于隔离潜在恶意文件的重要机制。
同源策略会限制不同源的页面获取如下数据:
- Cookie、LocalStorage 和 IndexDB
- DOM
- AJax请求
1.2 目的
虽然同源策略也会阻隔一些正常的请求(正是后面将谈到的跨域请求),但是它却是浏览器安全的基石。同源策略的目的是为了保证用户信息的安全(主要是 Cookie 信息),防止恶意网站窃取数据。
假如我们登陆网站A,网站A会用 Cookie 记录我们的身份,如果这时我们又浏览了恶意网站B,如果没有同源策略,网站B就可以读取网站A记录的代表我们身份的Cookie,从而伪装成我们去访问网站A。这样不仅我们的个人隐私得不到保障,更可能威胁我们电子账户的安全。
1.3 判断是否同源
判断两个页面是否同源的充要条件是:
- 协议相同
- 域名(主机)相同
- 端口相同
相对 http://127.0.0.1/index.html
检测下列页面是否同源:
URL | 结果 | 原因 | |
---|---|---|---|
http://127.0.0.1/page1.html | 同源 | 只有路径不同 | |
https://127.0.0.1/index.html | 不同源 | 协议不同(http->https) | |
http://127.0.0.1:3000/index.html | 不同源 | 端口不同(80->3000) | |
http://127.0.0.2/index.html | 不同源 | 主机不同(127.0.0.1->127.0.0.2) |
IE 特殊性
- 两个高度互信的域名,如公司域名,不受同源策略限制
- 只是端口不同的两个URL属于同源并且不受任何限制
2. 跨域请求解决方案
让我们进入正题。
在生产开发时,web服务器与数据服务器通常部署在两个服务器上,这意味着它们主机不同,如果 web服务器上的某个页面请求数据服务器的接口就属于跨域请求。如果使用和同源一样配置的 Ajax请求是不可能获得数据的。我们不妨来做一个小实验。
服务器-01_serverStart.js(node + express)
由于接下来的几种方法会不停变动服务器的配置,建议使用 supervisor
热部署
const express = require('express')
const app = new express()
// 监听端口为3000
app.listen(3000, function () {
console.log('Service Started ')
})
// 请求路径为 'account' 的 get 请求
app.get('/account', function (req, res) {
res.send('Account balance: 100')
})
// 请求路径为 'withdraw' 的 post 请求
app.post('/withdraw', function (req, res) {
const money = req.body
res.send('Account balance minus' + money)
})
客户端-01_跨域请求初体验.html(使用 axios 发送 Ajax 请求)
<body>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
<script type="text/javascript">
// 使用 axios 发送 Ajax 请求
axios.get('http://127.0.0.1:3000/account').then(response => {
console.log(response)
})
</script>
</body>
不出所料,浏览器返回了错误信息
接下来我们将关注于如何解决跨域请求,获取数据。
2.1 JSONP
2.1.1 实现原理
JSONP(JSON With Padding) 即填充式JSON,是利用<script>
标签不受同源策略限制的特性实现的。不受同源策略限制的常用标签还有:
<link>
<img>
<video> & <audio>
<frame> & <iframe>
JSON的工作原理示意图如下:
- 客户端利用
<script>
标签向非同源服务器发送请求,并传递一个回调函数Callback
作为请求参数 - 服务器处理请求,将数据作为实参填充
Callback
(填充式JSON由此得名),返回"Callback(data1, data2)"
(注意是字符串) - 浏览器接收到响应,解析执行
Callback(data1, data2)
2.1.2 实现
我们使用原生JS实现一个简单的JSONP
服务器-02_JSONP.js
app.get('/account', function (req, res) {
let {
callback}