你的位置:首页 > Java教程

[Java教程]JavaScript跨域研究


背景

         随着JavaScript引擎和浏览器的日益强大,现在使用JavaScript来构建庞大的应用已经屡见不鲜,而且也越来越流行。特别是近年来移动终端的快速发展,web前端不再是由一个个页面组成,更多的是一个页面即一个应用(单页应用)。一个大型的应用,特别是在企业中,都会与多个后台系统进行交互,此时会出现一种情况,一个页面与多个后台系统进行交互,这就会出现跨域问题。

 

跨域原因-------同源策略

         跨域的出现是因为浏览器存在同源策略。

         同源策略是浏览器对JavaScript代码能够操作哪些web内容的一条完整的安全限制。同源策略负责管理窗口或窗体中的JavaScript代码以及和其他窗体的交互。脚本只能读取和所属文档来源相同的窗口和文档的属性。

         文档的来源包含协议、主机,以及载入文档的URL端口。从不同Web服务器载入的文档具有不同的来源。通过同一个主机的不同端口载入的文档具有不同的来源。使用http:协议载入的文档和使用https协议载入的文档具有不同的来源,即便它们是同一台服务器。

     同源策略是为了防止恶意脚本窃取私有的信息。如果没有这一限制,恶意脚本可能打开一个空的窗口,欺骗用户进入并使用这个窗口阅读文件。恶意脚本就能够读取窗口的内容并将其发送回自己的服务器。同源策略就是为了防止这种行为。

         但是,在有些情况下,同源策略显的太过于严格了。会给那些使用多个子域的大站点带来一些问题,特别是对于我们目前大范围使用的ajax请求,因为ajax请求是依赖于对象,

 

解决方案

         受到同源策略的影响,跨域资源共享就会受到制约。但是随着人们的实践和浏览器的进步,目前在跨域请求的技巧上,有很多宝贵经验的沉淀和积累。下面我列举了几种较常用的跨域解决方案:JSONP、跨域资源共享(Cross-Origin Resource Sharing)、跨文档消息、Iframe等。其中CORS技术已经标准化,是W3C推荐使用的一种跨域请求方案,而跨文档消息则是使用window对象的postMessage()方法,可以异步传递消息事件。

JSONP:

         JSONP并不是一个官方的协议,它是利用script标签中src属性具有跨域加载资源的特性,而衍生出来的跨域数据访问方式。例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在 pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。

     JSONP的优点是:它不像

 

CORS

      跨域资源共享(Cross-Origin Resource Sharing)。这个标准草案采用新的“Origin:”请求头和新的Access-Control-Allow-Origin响应头来扩展HTTP。它允许服务器用头信息显式地列出源,或使用通配符来匹配所有的源并允许由任何地址请求文件。

      跨域资源共享为Web服务器定义了一种方式,允许网页从不同的域访问其资源。而这种访问是被同源策略所禁止的。CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。

     目前绝大部分的现代浏览器都支持CORS的跨域资源共享。

各浏览器支持如下图:

 

详细实现请看附录1。

附录1

         一、发起请求的服务器在发起ajax之前需要将withCredentials这个参数设置成true。

原生ajax:

 

Var xhr = new

var url = 'https://ebank-stg-shdmz.pingan.com.cn/financing/newccuser/getSetAccountControllerRest.do';

                                                     crossDomainRequest();

                                                 function crossDomainRequest() {

                                                   console.log("开始进行请求……");

                                                      if (xhr) {

                                                        xhr.open('post', url, true);

                                                        xhr.onreadystatechange = handler;

                                                       xhr.withCredentials = true;

                                                       xhr.send();

                                                      } else {

                                                        console.log("不能创建 。") ;

                                                      }

                                                    }

                                                    function handler(evtXHR) {

                                                      if (xhr.readyState == 4) {

                                                       if (xhr.status == 200) {

                                                          var response = xhr.responseText;

                                                         console.log("结果:" + response);

                                                        } else {

                                                                console.log("<br/>执行状态 status:" + xhr.status);

                                                        }

                                                     }

                                                      else {

                                                           console.log("<br/>执行状态 readyState:" + xhr.readyState);

                                                     }

                                                    }

Jquery的设置:

$.ajaxSetup({

        xhrFields: {

            withCredentials: true

        }

    });

 

 

Zepto的设置:

$.ajax({

              data : data,

              url :url,

              type : 'post',

              success : success,

              error : error,

            beforeSend:function(xhr){

                 xhr.withCredentials = true;

                 }

           });

 

二、目标服务器的改造

         1、需要所有被请求的接口对发起请求的服务器的域名进行域信任,以及设置访问控制允许凭证。

        

response.setHeader("Access-Control-Allow-Credentials","true");

response.addHeader("Access-Control-Allow-Origin",”https://ibank.pingan.com.cn”);

 

 

Access-Control-Allow-Origin设置的必须是一个且是全域名,如果端口号不是80,还需要加上端口号。

 

 

根据上述改造之后,就能解决ibank.pingan.com.cn跨域向ebank.pingan.com.cn请求数据且携带cookie过去的问题。

 

三、三个参数详情

         1、Access-Control-Allow-Origin

Access-Control-Allow-Origin 头来控制哪些域名的脚本可以访问该资源。如果设 置 Access-Control-Allow-Origin:*,则允许所有域名的脚本访问该资源。如果需要信任指定某个域,则必须将该域名以及端口号完整的写入,目前尚不支持同时信任多个域。不过我们可以动态进行对域的信任,变相信任多个域名,但是具体方案尚未测试通过。

 

2、Access-Control-Allow-Credentials与withCredentials

这两个参数都是对凭证的设置。

在跨域请求中,默认情况下是不发送验证信息的。要想发送验证信息,需要前台对withCredentials 属性进行设置,目标服务器对Access-Control-Allow-Credentials进行设置。

    这两个参数设置时,Access-Control-Allow-Origin是不能设置为*,否则浏览器会拒绝跨域访问。