首页 > 技术文章 > WEB跨域问题

xj-blog 2018-01-15 17:03 原文

跨域主要分为ajax跨域和iframe跨域:

  ajax跨域:在一个window中请求另一个项目的数据,域名发生改变,即为ajax跨域。要支持ajax跨域有几种方式,如:jsonp,CORS等。这里主要介绍CORS方式。

  iframe跨域:iframe本身是支持跨域的,即在一个域名下的window支持请求另一个域名下的数据显示到iframe,而不会产生跨域问题。但此跨域iframe中不允许访问父window中的属性和函数。即parent.var和top.var和parent.fn和top.fn会出现跨域异常,此时主要解决的是跨域数据传输问题。

 

CORS跨域:

  CORS跨域的主要思想是自定义HTTP响应头从而让浏览器和跨域服务器通信。默认情况下跨域访问时在ajax和http协议中都是不被支持的,为啦支持跨域ajax跨域,请求和响应都必须设置跨域支持参数。

  请求参数:使用jquery时设置crossDomain:true,如果要携带cookie需增加参数xhrFields: {withCredentials: true}。则请求格式为:

 1 $.ajax({
 2             crossDomain:true,
 3             xhrFields: {
 4                  withCredentials: true
 5             },
 6             url:url,
 7             data : data,
 8             success: function(data, statusText, xhr){
 9                    
10             }
11    }); 

 

  响应参数:响应时浏览器接收响应的响应头的响应域名与当前域名不一致,也是不被接受的,会报跨域异常,除非响应头中包含该域名的访问控制允许的参数

   HttpServletResponse response = (HttpServletResponse) res;  
        response.setHeader("Access-Control-Allow-Origin", "http://fsp1.uce.cn:8005");

   如果要允许跨域携带cookie,设置响应头Access-Control-Allow-Credentials属性为true
        response.setHeader("Access-Control-Allow-Credentials", "true");

  为了统一响应参数,不用在每个controller中设置响应头。一般通过Filter实现响应头的统一处理

 1 import java.io.IOException;
 2 
 3 import javax.servlet.Filter;
 4 import javax.servlet.FilterChain;
 5 import javax.servlet.FilterConfig;
 6 import javax.servlet.ServletException;
 7 import javax.servlet.ServletRequest;
 8 import javax.servlet.ServletResponse;
 9 import javax.servlet.http.HttpServletResponse;
10 
11 public class AccessFilter implements Filter {  
12   
13     @Override  
14     public void init(FilterConfig filterConfig) throws ServletException {  
15           
16     }  
17   
18     @Override  
19     public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {  
20           
21         HttpServletResponse response = (HttpServletResponse) res;  
22         response.setHeader("Access-Control-Allow-Origin", "http://fsp1.uce.cn:8005");  
23         response.setHeader("Access-Control-Allow-Credentials", "true");  
24         /*response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");  
25         response.setHeader("Access-Control-Max-Age", "3600");  
26         response.setHeader("Access-Control-Allow-Headers", "x-requested-with");  */
27         chain.doFilter(req, res);  
28     }  
29   
30     @Override  
31     public void destroy() {  
32           
33     }  
34   
35 }  


  这样ajax请求就实现啦可带cookie的跨域交互。

  

  iframe跨域:iframe原生支持跨域。跨域iframe主要难在和父窗口进行数据交互。

    父窗口向跨域iframe传输数据:主要分为postMessage和window.name,postMessage只能在窗口加载完成后才能使用,如果是固定的iframe标签,可直接获取iframe.postMessage,如果是动态添加的iframe必须监听iframe的load事件,在该事件中postMessage。window.name当然也必须在window存在后才能设置,也要设置在iframe的load事件中,但window.name属性有个特点是只要window存在,doument重新roload的时候,window.name是不变的,因此,对于要在iframe加载过程中使用的数据,我们可以先让iframe的window load一次,此次load是一个空白页面,当空白页面load完成事件设置window.name属性,在设置window.name属性后再修改iframe的src让此window重新加载目标跨域页面,这时在该跨域页面加载过程中就个通过window.name获取到父页面传输的数据。

      主要有两种情况:

        1>传输的数据在此跨域iframe加载完成后内部触发事件时使用,可通过postMessage传输,此方法较为简单

        

var state = 1;
document.getElementById('#iframeId').addEventListener("load", function() { if(state == 1) { state = 0; this.contentWindow.postMessage(data,crossDomain) } }, true);

         2>传输的数据在此跨域iframe加载过程中调用,此时不能在使用postMessage,该方法只能执行于窗口加载完成后,即load事件后,但在加载过程中如果获取参数,该参数还未传递过来,此时必须使用window.name传递,window的name属性在窗口重新加载内容的时候是不会改变的,最大支持2M。既然要在窗口load时就要读取参数,那么该参数必须要在窗口load之前设置进去,所以要首先load一次窗口,此次load的url为一空白中间页面,目的只是让window加载并且设置window.name属性,在设置之后改变iframe的url为我们的目标跨域url,则iframe从新出发load事件,此时load的过程中就可以获取上次load中间页面设置的window.name属性就获取到了父窗口的值

  跨域iframe向父窗口传递数据“

    此时iframe已经加载完成,使用postMessage比较方便,父窗口监听跨域iframe的messge,进行相应的处理,比如调用自身的函数

    

window.onload = function() {
    if(window.top == window.self) {
        //当前窗口为最顶层窗口
        return;
    } else {
        //折叠消息面板
        document.body.onclick = function(event) {
            //如果直接调用父页面成功,则直接调用,如果调用失败则为跨域,向父页面传递消息
            try {
                if(window.top.vm.settings.type) {
                    window.top.vm.openSetting(event);
                }
            } catch(e) {
                window.top.postMessage('click', '*');
            }
        }

        //皮肤设置
        var settings = localStorage.settings;
        var themes = settings ? JSON.parse(settings) : {
            themes: "default"
        }
        var css = document.createElement('style');
        css.type = 'text/css';
        css.id = "themes-setting";
        if(themes.themes == 'default') {
            css.innerHTML = ".iconfont{color:#ff9372;}";
        } else if(themes.themes == 'blue') {
            css.innerHTML = ".iconfont{color:#23b7e5;}";
        } else {
            css.innerHTML = ".iconfont{color:#464c5b;}";
        }
        document.head.appendChild(css);
    }

}
/**
                 * 监听跨域iframe传递的消息
                 */
                window.addEventListener('message', function(e) {
                    var operate = e.data;
                    switch(operate) {
                        case 'click':
                            debugger
                            if(vm.settings.type){
                                vm.openSetting();
                            }
                            break;
                        default:
                            break;
                    }

                })

 

推荐阅读