前言:对于跨域请求,很早以前就有去了解过,但由于一直关注的都是服务器后端开发,故也就仅仅停留在概念的理解上而没有机会在实际开发场景中接触获得。最近在公司的开发任务中,须要接触到 Ajax 跨域请求,因为以前没有遇到过相似的问题,在开发过程当中遇到很多困难,也查阅了很多资料和博客。在这过程当中收获了很多,故特地写下如下文章总结,若是文章有什么不足之处,还望各位指出。javascript
在 HTML 中,<a>
, <form>
, <img>
, <script>
, <iframe>
, <link>
等标签以及 Ajax 均可以指向一个资源地址,而所谓的跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不同。这里的域指的是这样的一个概念:咱们认为若协议 + 域名 + 端口号均相同,那么就是同域。php
举个例子:假如一个域名为aaa.cn
的网站,它发起一个资源路径为aaa.cn/books/getBookInfo
的 Ajax 请求,那么这个请求是同域的,由于资源路径的协议、域名以及端口号与当前域一致(例子中协议名默认为http,端口号默认为80)。可是,若是发起一个资源路径为bbb.com/pay/purchase
的 Ajax 请求,那么这个请求就是跨域请求,由于域不一致,与此同时因为安全问题,这种请求会受到同源策略限制。html
一般,浏览器会对上面提到的跨域请求做出限制。浏览器之因此要对跨域请求做出限制,是出于安全方面的考虑,由于跨域请求有可能被不法分子利用来发动 CSRF攻击。java
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。CSRF攻击者在用户已经登陆目标网站以后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操做的请求,达到攻击目的。web
CSRF 攻击的原理大体描述以下:有两个网站,其中A网站是真实受信任的网站,而B网站是危险网站。在用户登录了受信任的A网站是,本地会存储A网站相关的Cookie,而且浏览器也维护这一个Session会话。这时,若是用户在没有登出A网站的状况下访问危险网站B,那么危险网站B就能够模拟发出一个对A网站的请求(跨域请求)对A网站进行操做,而在A网站的角度来看是并不知道请求是由B网站发出来的(Session和Cookie均为A网站的),这时便成功发动一次CSRF 攻击。ajax
于是 CSRF 攻击能够简单理解为:攻击者盗用了你的身份,以你的名义发送而已请求。CSRF可以作的事情包括:以你名义发送邮件,发消息,盗取你的帐号,甚至于购买商品,虚拟货币转帐......形成的问题包括:我的隐私泄露以及财产安全。json
所以,大多数浏览器都会跨域请求做出限制,这是从浏览器层面上的对 CSRF 攻击的一种防护,可是须要注意的是在复杂的网络环境中借助浏览器来防护 CSRF 攻击并不足够,还须要从服务端或者客户端方面入手防护。详细能够参考这篇文章浅谈CSRF攻击方式后端
<script><img><iframe><link><video><audio>
等带有src属性的标签能够从不一样的域加载和执行资源。flash、java applet、silverlight、googlegears
等浏览器加载的第三方插件也有各自的同源策略,只是这些同源策略不属于浏览器原生的同源策略,若是有漏洞则可能被黑客利用,从而留下XSS攻击的后患虽然在安全层面上同源限制是必要的,但有时同源策略会对咱们的合理用途形成影响,为了不开发的应用受到限制,有多种方式能够绕开同源策略,下面介绍的是常用的 JSONP, CORS 方法。api
<script><img><iframe>
等标签不受同源策略限制,能够从不一样域加载并执行资源的特性,来实现数据跨域传输。首先当前页面中声明有这样的一个函数,它将做为 JSONP 的回调函数处理做为函数参数传入的数据跨域
<script type="text/javascript"> function dosomething(jsondata){ //处理得到的json数据 } </script>
而后,咱们就能够借助 <script><img><iframe>
等标签能够引入不一样域资源的特性,将须要发送的请求的路径做为src参数,其中须要注意的是:须要告知服务端回调函数的函数名。
<script src="http://example.com/data.php?callback=dosomething"></script>
这时服务端在返回数据的时候,就会返回一端 Javascript 代码,在 Javascript代码中调用了回调函数,而且须要返回的数据做为回调函数的参数
dosomething(['a','b','c']);
最后页面成功加载了刚才指定路径的资源后,将会执行该 Javascript 代码,dosomething
函数将执行,这时一次跨域请求完成。
另外,若是页面引入了 jQuery,那么能够经过它封装的方法很方便的实现JSONP操做了
// Using YQL and JSONP $.ajax({ url: "http://query.yahooapis.com/v1/public/yql", // The name of the callback parameter, as specified by the YQL service jsonp: "callback", // Tell jQuery we're expecting JSONP dataType: "jsonp", // Tell YQL what we want and that we want JSON data: { q: "select title,abstract,url from search.news where query=\"cat\"", format: "json" }, // Work with the response success: function( response ) { console.log( response ); // server response } });
JSONP 的优势是:它不像XMLHttpRequest
对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中均可以运行。
JSONP 的缺点是:它只支持 GET 请求,而不支持 POST 请求等其余类型的 HTTP 请求
跨源资源共享 Cross-Origin Resource Sharing(CORS) 是一个新的 W3C 标准,它新增的一组HTTP首部字段,容许服务端其声明哪些源站有权限访问哪些资源。换言之,它容许浏览器向声明了 CORS 的跨域服务器,发出 XMLHttpReuest 请求,从而克服 Ajax 只能同源使用的限制。
另外,规范也要求对于非简单请求,浏览器必须首先使用 OPTION 方法发起一个预检请求(preflight request),从而获知服务端是否容许该跨域请求,在服务器肯定容许后,才发起实际的HTTP请求。对于简单请求、非简单请求以及预检请求的详细资料能够阅读HTTP访问控制(CORS) 。
下面对 CORS 中新增的 HTTP 首部字段进行简析:
Access-Control-Allow-Origin
响应首部中能够携带这个头部表示服务器容许哪些域能够访问该资源,其语法以下:
Access-Control-Allow-Origin: <origin> | *
其中,origin 参数的值指定了容许访问该资源的外域 URI。对于不须要携带身份凭证的请求,服务器能够指定该字段的值为通配符,表示容许来自全部域的请求。
Access-Control-Allow-Methods
该首部字段用于预检请求的响应,指明实际请求所容许使用的HTTP方法。其语法以下:
Access-Control-Allow-Methods: <method>[, <method>]*
Access-Control-Allow-Headers
该首部字段用于预检请求的响应。指明了实际请求中容许携带的首部字段。其语法以下:
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
Access-Control-Max-Age
该首部字段用于预检请求的响应,指定了预检请求可以被缓存多久,其语法以下:
Access-Control-Max-Age: <delta-seconds>
Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否容许发送Cookie。默认状况下,Cookie不包括在CORS请求之中。设为true
,即表示服务器明确许可,Cookie能够包含在请求中,一块儿发给服务器。其语法以下:
Access-Control-Allow-Credentials: true
另外,若是要把 Cookie 发送到服务器,除了服务端要带上Access-Control-Allow-Credentials
首部字段外,另外一方面请求中也要带上withCredentials
属性。
可是须要注意的是:若是须要在 Ajax 中设置和获取 Cookie,那么Access-Control-Allow-Origin
首部字段不能设置为*
,必须设置为具体的 origin 源站。详细可阅读文章CORS 跨域 Cookie 的设置与获取
Origin
该首部字段代表预检请求或实际请求的源站。不论是否为跨域请求,Origin字段老是被发送。其语法以下:
Origin: <origin>
Access-Control-Request-Method
该首部字段用于预检请求。其做用是,将实际请求所使用的 HTTP 方法告诉服务器。其语法以下:
Access-Control-Request-Method: <method>
Access-Control-Request-Headers
该首部字段用于预检请求。其做用是,将实际请求所携带的首部字段告诉服务器。其语法以下:
Access-Control-Request-Headers: <field-name>[, <field-name>]*
假设咱们在 bbb.cn 域名下,发送一个 Ajax 请求到 aaa.cn 域名,其路径以下:http://aaa.cn/localserver/api/corsTest
。因为同源策略,这样的 Ajax 请求将会被浏览器所拦截,获得下面的信息:
若想可以发送跨域请求,咱们只须要在服务器的响应中配置适当的CORS HTTP 首部字段就能够了,例如能够加入如下的首部字段:
Access-Control-Allow-Methods:*
此时,Ajax请求就能够顺利的发送和接收了,对应的请求和响应头部以下:
对于在 Java Web 项目中,如何在 Servlet 或这 Spring MVC 中配置 CORS 能够阅读文章Spring MVC 实现 CORS 跨域 。