在我们日常开发中,经常能够听到‘跨域’这个字眼,那么跨域是什么呢?
跨域其实是由浏览器的同源策略造成的
那么同源策略又是什么呢?
同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
什么又是同源呢?
如果两个 URL 的 协议、域名、端口号都相同的话,则这两个 URL 是同源。
具体看下面的表格,与http://store.company.com/dir/page.html
的源进行对比
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同 ( http:// 默认端口是80) |
http://news.company.com/dir/other.html | 失败 | 主机不同 |
我们可以使用 CORS 来允许跨源访问。CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源
接着你可能会疑惑为什么浏览器需要同源策略
在日常工作中,我们快快乐乐的写好代码之后去调用某个接口,然后被告知,跨域,阻碍我们搬砖。试想下,如果没有同源策略,我们是不是就不用考虑什么所谓的跨域了呢?为什么浏览器非要搞出个同源策略来折磨我们呢?没有行不行呢?
举个例子:我们都知道有个东西叫cookie,一般用来处理登录这种场景,目的是让服务端知道谁发出的这次请求,如果你请求了接口进行登录,服务端验证通过后会在响应头加入cookie字段,下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求的头字段Cookie中,服务端就能知道这是哪个用户,并且这个用户已经登录过了。比如你在电脑上登录了你的支付宝账号,查看自己的小金库,这个时候有人给你发了个链接,然后你就不小心点了这个链接。由于没有同源策略的限制,它向你的支付宝发起了请求~~~。这个时候你一定想到:服务端验证通过后会在响应头加入cookie字段,然后下次再发请求的时候,浏览器会自动将cookie附加在HTTP请求头的字段中,这样一来,这个不法网站就相当于登录了你的账号,穿上了你的‘衣服’,然后就可以对你的支付宝为所欲为~
上面这个例子就是大名鼎鼎的CSRF(Cross-site request forgery),中文名称:跨站请求伪造,即:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。
看到这你可能又想,即使有了同源策略限制,不法分子也能通过方法拿到cookie,这个我简单的看了一下,服务端可以设置httpOnly,让前端无法操作cookie,这样就可以避免这个问题,或者就是设置secure,保证在https的加密通信中传输以防cookie被截获。
这就说的有点远了,言归正传。我们知道同源策略是浏览器为了避免一些不法分子做坏事而做的一种防御手段,但是像我们这种积极向上的搬砖工,就要自己想办法去解决跨域了
CORS即Cross Origin Resource Sharing(跨源资源共享),通俗说就是我们所熟知的跨域请求,它使用额外的 HTTP 头部告诉浏览器可以让一个web应用进行跨域资源请求。那么具体的做法是什么呢???
先说说两种请求方式吧,具体的可以见博主的另外一篇小博文:简单请求和复杂请求,
简单直白的说:简单请求的请求方式只能是:head、
get、
post,
请求头允许的字段为:Accept、
Accept-Language、
Content-Language、
Last-Event-ID,
Content-Type为
:application/x-www-form-urlencoded、multipart/form-data、text/plain 三选一。不满足上面的,都是复杂请求。下面详细说下两者的机制
简单请求
浏览器发送请求,由于同源策略会产生跨域,所以呢,我们在请求的时候得往请求头里添加一个标识即:origin,告诉服务器自己的身份。origin相当于你进皇宫时候亮的令牌,服务器就是门口的守卫,认牌不认人。如果你的牌子有效,那么放你进去,并且会使用Access-Control-Allow-Origin
,告诉你允许哪个域发送请求,该字段是必须的,它的值要么是请求时Origin
字段的值,要么是一个*
,表示接受任意域名的请求。
复杂请求
最常见的情况,比如当我们使用put
请求时,浏览器会先发送option
(预检)请求,不过有时候,你会发现并没有,这是个我了解了下是因为缓存,后面再进行详细的调研,这里先说结果。
预检请求与简单请求不同的是,option请求多了2个字段:Access-Control-Request-Method
:该次请求的请求方式Access-Control-Request-Headers
:该次请求的自定义请求头字段
注意:Access-Control-Request-Method
,Access-Control-Request-Headers
返回的是满足服务器要求的所有请求方式,请求头,不限于该次请求
再来详细说下这个origin:
Origin
表示请求来自于哪个站点,并不包含任何路径信息。基本上,浏览器会将Origin请求头添加到所有跨域的请求中,同域时发送post请求,才会携带origin请求头。
Origin
除了不包含路径信息,该字段与 Referer
首部字段相似。如果浏览器不能获取请求源,但是满足上面的条件,那么也会携带origin,只不过值为null。可是浏览器如果不能获取请求源,那么请求头中不会携带referer。origin的值只包括协议、域名和端口,而rerferer不但包括协议、域名、端口还包括路径,参数,注意不包括hash值
origin 语法
Origin: ""
Origin: <scheme> "://" <host> [ ":" <port> ]
origin 声明
<scheme>
请求所使用的协议,通常是HTTP协议或者它的安全版本HTTPS协议。
<host>
服务器的域名或 IP 地址。
<port> 可选
服务器正在监听的TCP 端口号。缺省为服务的默认端口(对于 HTTP 请求而言,默认端口为 80)。
接着再给大家讲个例子吧:
我们知道啊,图片请求一般是不会受跨域影响的,不管是我们的script标签的src还是img标签的src,或者说link标签的href他们没有被同源策略所限制,比如我们有可能使用一个网络上的图片,就可以请求得到,因为src或href链接的静态资源,本质上来说也是一个get请求。
之前在工作中,有同事遇到过一个问题, 图片请求的时候报跨域错误了,代码如下:
# 在 csdn 中访问 cnblog 图片
var image = new Image();
image.crossOrigin = '';
image.src = 'http://images2015.cnblogs.com/blog/980037/201607/980037-20160707132753546-1822390554.png';
能不能看出来问题,我们能看到一个关键词 crossorigin ,关于 crossOrigin,我们看看 MDN 的解释:
在HTML5中,一些 HTML 元素提供了对 CORS 的支持, 例如 <audio>
、<img>
、<link>
、<script>
和 <video>
均有一个跨域属性 (crossOrigin
property),它允许你配置元素获取数据的 CORS 请求。
直白的说:
- 加了 crossorigin 属性,则表明图片就一定会按照 CORS 来请求图片。
- 可以设置的值有 anonymous 以及 use-credentials,它们的作用都是设置通过 CORS 来请求图片,区别在于 use-credentials 是加了凭证的 CORS
- 如果默认用户不进行任何设置,那么就不会发起 CORS 请求。但如果设置了除 anonymous 和 use-credentials 以外的其他值,包括空字串在内,默认会当作 anonymous来处理
现在再看上面的问题,就知道为什么请求图片会报跨域了吧,就是因为设置了image.crossOrigin = ''
再来详细分析下这个问题
当我设置了crossOrigin去请求图片的时候会产生跨域行为,请求头如下所示:
再来看看,不设置crossOrigin去请求相同的图片:
注意:我们是不可以手动去加no-cros的,这是浏览器自己的行为,如果手动去改,会被认为成cors
JSONP与CORS的使用目的相同
JSONP只支持GET
请求,CORS支持所有类型的HTTP请求,但是jsonp的兼容性比较好,而cors的兼容性比较差,如下图所示:
其他的解决跨域的方法可以参考我的另外两篇博客:
本文参考:阮一峰跨域资源共享 CORS 详解