最近在做一个项目,需要用到跨域访问,在这里将解决问题的过程与大家分享一下。
javascript出于安全方面的考虑,使用同源策略,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。
举例如下:
url1
url2
说明
是否允许通信
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下
允许
http://www.a.com/lab/a.js
http://www.a.com/script/b.js
同一域名下不同文件夹
http://www.a.com:8000/a.js
同一域名,不同端口
不允许
https://www.a.com/b.js
同一域名,不同协议
http://127.0.0.1/b.js
域名和域名对应ip
http://script.a.com/b.js
主域相同,子域不同
http://a.com/b.js
同一域名,不同二级域名(同上)
不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js
不同域名
对于跨域的问题,在网上也有很多解决方案,很多人都建议使用<code>jsonp</code>,这种方式我认为需要对服务端进行改造,代价稍高,所以在这篇博客中我们不讨论,有兴趣可以查看相关文档,今天我们主要来讨论如何使用响应头的方式来解决跨域的问题。
首先我们先来看看,跨域到底是个什么问题,我显现编写一个服务端的程序用于测试(使用<code>servlet</code>),
在<code>web.xml</code>中进行相关配置使其生效:
配置本地host为:<code>www.jianggujin.com</code>,使用浏览器访问<code>http://www.jianggujin.com/web/cd</code>测试一下,浏览本期会显示:<code>{"name":"jianggujin"}</code>。
接下来我们在编写一个本地的网页:
访问页面,浏览器控制台显示如下信息(注:显示新格式可能会不同,以具体环境为准):
<code>[web浏览器] "xmlhttprequest cannot load http://www.jianggujin.com/web/cd. no 'access-control-allow-origin' header is present on the requested resource. origin 'http://127.0.0.1:8020' is therefore not allowed access." /mui/crossdomain.html (0)</code>
这就是跨域导致的问题,同样的地址我们使用浏览器可以直接访问,但是使用ajax请求的时候,由于同源策略就被限制了,请求不被允许。通过错误信息,我们也可以得到一点解决问题的消息,缺少:<code>access-control-allow-origin</code>头。
在w3c中对该问题也做了描述,并列举了一些头,下述内容摘自原文:
5 syntax this section defines the syntax of the new headers this specification introduces. it also provides a short description of the function of each header. the resource processing model section details how resources are to use these headers in a response. likewise, the user agent processing model section details how user agents are to use these headers. the abnf syntax used in this section is from http/1.1. [http] http/1.1 is used as abnf basis to ensure that the new headers have equivalent parsing rules to those introduced in that specification. http/1.1 currently does not make leading ows implied in header value definitions but that form is assumed here. 5.1 access-control-allow-origin response header the access-control-allow-origin header indicates whether a resource can be shared based by returning the value of the origin request header, “*”, or “null” in the response. abnf: access-control-allow-origin = “access-control-allow-origin” “:” origin-list-or-null | “*” in practice the origin-list-or-null production is more constrained. rather than allowing a space-separated list of origins, it is either a single origin or the string “null”. 5.2 access-control-allow-credentials response header the access-control-allow-credentials header indicates whether the response to request can be exposed when the omit credentials flag is unset. when part of the response to a preflight request it indicates that the actual request can include user credentials. abnf: access-control-allow-credentials: “access-control-allow-credentials” “:” true true: %x74.72.75.65 ; “true”, case-sensitive 5.3 access-control-expose-headers response header the access-control-expose-headers header indicates which headers are safe to expose to the api of a cors api specification. abnf: access-control-expose-headers = “access-control-expose-headers” “:” #field-name 5.4 access-control-max-age response header the access-control-max-age header indicates how long the results of a preflight request can be cached in a preflight result cache. abnf: access-control-max-age = “access-control-max-age” “:” delta-seconds 5.5 access-control-allow-methods response header the access-control-allow-methods header indicates, as part of the response to a preflight request, which methods can be used during the actual request. the <code>allow</code> header is not relevant for the purposes of the cors protocol. abnf: access-control-allow-methods: “access-control-allow-methods” “:” #method 5.6 access-control-allow-headers response header the access-control-allow-headers header indicates, as part of the response to a preflight request, which header field names can be used during the actual request. abnf: access-control-allow-headers: “access-control-allow-headers” “:” #field-name 5.7 origin request header the origin header indicates where the cross-origin request or preflight request originates from. [origin] 5.8 access-control-request-method request header the access-control-request-method header indicates which method will be used in the actual request as part of the preflight request. abnf: access-control-request-method: “access-control-request-method” “:” method 5.9 access-control-request-headers request header the access-control-request-headers header indicates which headers will be used in the actual >request as part of the preflight request. abnf: access-control-request-headers: “access-control-request-headers” “:” #field-name
看不懂?不着急,我们急需解决问题,上面的一大串内容的意思就是提供了这些响应头用具解决ajax的跨域问题。当ajax进行跨域访问时,浏览器首先会进行一次option请求判断该请求是否被循序跨域,当响应结果为允许访问时,再进行真正的请求。
下面我们对servlet进行修改使其允许跨域:
再次打开网页,观察控制台输出:

通过输出,我们发现这时候已经可以正常访问了,这样我们就满足了吗?如果有cookie之类的信息也可以正常提交吗?我们来继续测试,在此更改服务端代码:
我们先用浏览器直接访问地址,访问两次,观察控制台输出:
然后我们在通过网页访问看看结果:
两次结果对比,我们可以发现,虽然我们解决了跨域,但是cookie信息并没有提交,当然了我们也是有办法解决的:
服务端:
网页:
这个时候我们再访问网页就会达到我们想要的结果了。
到这里,我们基本上就解决了跨域的问题了,在实际应用中,我们可能还会遇到其他的问题,比如请求头不允许跨域等,解决方法都是类似的,我们只要添加相应的头信息就可以了,因为测试的原因,响应头信息我是直接放在了servlet中进行处理,在实际应用中,这样做就很麻烦了,我们可以编写一个过滤器用于跨域访问,在这里就不贴代码了,大家可以自己思考,对该问题进行为你善解决。