一、跨域问题产生的原因
浏览器的同源策略。
二、没有同源策略限制的两大危险场景
浏览器是从两个方面去做同源策略的,一是针对接口的请求,二是针对DOM的查询。试想一下没有这样的限制上述两种动作有什么危险。
2.1、没有同源策略限制的接口请求
Cookie大家应该知道,一般用来处理登录等场景,目的是让服务端知道谁发出的这次请求。如果你请求了接口进行登录,服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将Cookie附加在HTTP请求的头字段Cookie中,服务端就能知道这个用户已经登录过了。知道这个之后,我们来看场景:
你准备去清空你的购物车,于是打开了买买买网站www.maimaimai.com,然后登录成功,你在看有什么东西买的过程中,你的朋友发给你一个链接www.nidongde.com,你饶有兴致地浏览着www.nidongde.com,谁知这个网站暗地里做了些不可描述的事情!由于没有同源策略的限制,它向www.maimaimai.com发起了请求!聪明的你一定想到上面的话“服务端验证通过后会在响应头加入Set-Cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中”,这样一来,这个不法网站就相当于登录了你的账号,可以为所欲为了!这就是传说中的CSRF攻击。
2.2、没有同源策略限制的DOM查询
有一天你刚睡醒,收到一封邮件,说是你的银行账号有风险,赶紧点进www.yinghang.com改密码。你赶紧点进去,还是熟悉的银行登录界面,你果断输入你的账号密码,登录进去看看钱有没有少了。睡眼朦胧的你没看清楚,平时访问的银行网站是www.yinhang.com,而现在访问的是www.yinghang.com,这个钓鱼网站做了什么呢?
1
2
3
4
5
6
7
8
9
// HTML
<iframe name="yinhang" src="www.yinhang.com"></iframe>
// JS
// 由于没有同源策略的限制,钓鱼网站可以直接拿到别的网站的Dom
const iframe = window.frames['yinhang']
const node = iframe.document.getElementById('你输入账号密码的Input')
console.log('拿到了这个${node},还拿不到刚刚输入的账号密码吗')
三、同源策略限制下接口请求的正确打开方式
3.1、JSONP
在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,利用这一点,我们可以实现跨域通信。JSONP全称json with padding,填充式的JSON。
3.1.1、JSONP的原理
- 首先是利用script标签的src属性来实现跨域。
- 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后再返回,实现服务器端向客户端通信。
- 由于使用script标签的src属性,因此只支持GET方法。
3.1.2、支持跨域的标签
1
2
3
<img src=""> // 图片
<link href=""> // CSS
<script src=""> // 程序
3.1.3、JQuery中的JSONP
JQuery中的$.ajax()
函数,除了可以发起真正的Ajax数据请求之外,还可以发起JSONP数据请求:
$.ajax({
url: 'http://www.liulongbin.top:3006/api/jsonp?name=zs&age=20',
dataType: 'jsonp',
success: function (res) {
console.log(res);
}
})
默认情况下,使用JQuery发起JSONP请求,会自动携带一个callback=jQueryxxx
的参数,jQueryxxx是随机生成的一个回调函数的名称。在JQuery中如果想要自定义JQuery的回调函数名称及JSONP的参数,可通过以下两个参数来指定:
$.ajax({
url: 'http://www.liulongbin.top:3006/api/jsonp?name=zs&age=20',
dataType: 'jsonp',
jsonp: 'callback', // 自定义参数的名称 一般让它默认为callback不会做修改
jsonpCallback: 'abc', // 自定义回调函数的名称
success: function (res) {
console.log(res);
}
})
JQuery中JSONP的实现过程:JQuery中的JSONP也是通过script标签的src属性来实现跨域数据访问的,只不过,JQuery采用的是动态创建和移除script标签的方式,来发起JSONP数据请求。在发起JSONP请求的时候,动态向<header>中append一个script标签;在JSONP请求成功以后,动态地移除刚刚append进去的script标签。
3.2、空iframe加form
3.3、CORS
CORS是一个W3C标准,全称是跨域资源共享(Cross-origin resource sharing)。看名字就知道这是处理跨域问题的标准做法。浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。非简单请求会发出一次预检测请求,返回码是204,预检测通过才会真正发出请求,这才返回200。只要同时满足以下两大条件,就属于简单请求。
- 请求方法是以下三种方法之一
- HEAD
- GET
- POST
- HTTP的头信息不超出以下几种字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
3.4、代理
Nginx配置:
1
2
3
4
5
6
7
8
9
10
server{
# 监听9099端口
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
location ^~ /api {
proxy_pass http: //localhost:9871;
}
}
Nginx转发的方式似乎很方便!但这种使用也是看场景的,如果后端接口是一个公共的API,比如一些公共服务获取天气什么的,前端调用的时候总不能让运维去配置一下Nginx,如果兼容性没问题(IE 10或者以上),CROS才是更通用的做法吧。