天天看点

运用cors解决跨域问题总结

最近在做的一个应用迁移项目,第一阶段要将工作台页面内的a应用所有链接,迁到b应用中,对应链接的域名也变为b应用的域名,因此产生了跨域问题。通常这种跨域问题可以采用jsonp请求解决,但是jsonp在本项目中会有几个难点:一是前端改动较大,二是jsonp只支持get请求,另外就是比较难执行灰度(jsonp和非jsonp的请求显然是不好作为灰度的a/b项的)。

    除了jsonp,还有一种比较新的技术就是cors了。cors是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制。使用cors,可以通过普通的xmlhttprequest发起请求和获得数据,并且支持各种类型请求。缺点就是因为属于比较新的技术,所以浏览器兼容性有一定局限。

运用cors解决跨域问题总结

      <b>由于这次改造的工作台只支持火狐和谷歌浏览器,所以cors的兼容性也就不是问题。如果是其他pc端的系统,在选择cors是就要考虑兼容性的影响了。</b>

      下面就是具体怎么实现cors。首先,对于支持cors的浏览器,发送请求时,如果我们观察请求头会发现带有"origin:http://example.taobao.com" ,该标识用来说明本次请求来自的域。要实现cors,只要在服务端在响应头部加上标识“access-control-allow-origin:http://example.taobao.com”,这样浏览器发现access-control-allow-origin和请求来自的域一致,就允许跨域访问资源example.taobao.com的资源了。对应服务端代码:

rundata.getresponse().setheader("access-control-allow-origin", "http://example.taobao.com");             

      如果access-control-allow-origin设置为"*",那么任何域的请求都可以通过cors访问服务资源,但是这样显然不够安全。我们可以在服务端设置一个域白名单,收到请求时先取请求头中origin标识的域,判断域是否在白名单中,如果在的话,再将该域设置到响应的access-control-allow-origin中。这样就实现指定某些域cors请求服务资源。

      服务端返回的响应除了加允许的请求域标识,同时对允许的请求方法也要标识,我们一般固定写为get和post就可以:  rundata.getresponse().setheader("access-control-allow-methods","get,post");

      同时需要设置:

      rundata.getresponse().setheader("access-control-allow-headers", rundata.getrequest().getheader("access-control-request-headers"));//意思是请求的headers是什么我就返回允许的headers什么

      需要注意的时,按照上面方法做,此时的cors请求可以访问服务,但是请求不会带cookie信息。如果需要cookie信息的话,需要请求js和服务端做一个通信验证:

      js请求: var invocation = new xmlhttprequest();  invocation.withcredentials = true;        

      服务端:rundata.getresponse().setheader("access-control-allow-credentials", "true");      

      该验证ie并不支持,即使是高版本支持cors的ie10,11,所以这也是个坑。需要兼容ie的应用基本不用考虑cors了。

      最后,需要说的是cors的一个重要特性,处理不好也是一个坑,就是探测请求。当跨域请求时,如果请求较为“复杂”(定义复杂的标准在最后会附上),则浏览器会先发一个options类型请求,域名和uri不变,但是不会带任何参数和cookie信息。这个请求的目的就是获得返回的响应后验证响应头,判断服务端是否支持该域的cors访问,支持的话才会发送真正的请求。所以叫探测请求。那么我们在服务端对这种options类型方法请求,显然要特殊处理,首先按照前面介绍设置响应头相关内容以支持cors,其次要让请求跳过执行业务方法直接返回结束,因为这只是个探寻请求。这块我们可以做一个公用模块,或拦截器来统一处理:

      if("options".equals(rundata.getrequest().getmethod())){

        //设置access-control-allow相关头

        rundata.getresponse().getwriter().flush();

        return true;

      }

如果不希望每次请求都多一次options请求,可以设置返回响应头信息:

      rundata.getresponse().setheader("access-control-max-age","60"); //缓存options结果60秒

最后附上cors“复杂”请求标准(满足一条即是):

      1、http请求方法不是head、get、post其中之一

      2、http请求头没有包含下列所有:accept、accept-language、content-language、last-event-id、content-type

      3、content-type不是下列类型:application/x-www-form-urlencoded、multipart/form-data、text/plain

继续阅读