浏览器跨域问题的总结

浏览器跨域问题的总结

目录

 

1.document.domain+iframe情形

2.jQuery下通过jsonp实现跨域

3.服务器端的跨域解决方案

4.IE8下的跨域问题

 


 

JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。但在安全限制的同时也给注入iframe或是ajax应用上带来了不少麻烦。我们在项目开发中遇到了几种跨域情形,现在进行一些总结,也对互联网上凌乱的东西进行一些总结,着重描述上述一些情景,以下列举跨域问题的几种情形

 

URL

说明

是否允许通信

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
http://www.a.com/b.js

同一域名,不同端口

不允许

http://www.a.com/a.js
https://www.a.com/b.js

同一域名,不同协议

不允许

http://www.a.com/a.js
http://70.32.92.74/b.js

域名和域名对应ip

不允许

http://www.a.com/a.js
http://script.a.com/b.js

主域相同,子域不同

不允许

http://www.a.com/a.js
http://a.com/b.js

同一域名,不同二级域名(同上)

不允许(cookie这种情况下也不允许访问)

http://www.cnblogs.com/a.js
http://www.a.com/b.js

不同域名

不允许

特别注意两点:

第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,

第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。

 1.document.domain+iframe情形

对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。具体的做法是可以在http://www.a.com/a.html和http://script.a.com/b.html两个文件中分别加上document.domain ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。当然这种办法只能解决主域相同而二级域名不同的情况,如果你异想天开的把script.a.com的domian设为alibaba.com那显然是会报错地!代码如下:

www.a.com上的a.html

document.domain 'a.com';

var ifr document.createElement_x('iframe');

ifr.src 'http://script.a.com/b.html';

ifr.style.display 'none';

document.body.appendChild(ifr);

ifr.onload function(){

    var doc ifr.contentDocument || ifr.contentWindow.document;

    // 在这里操纵b.html

    alert(doc.getElementsByTagName_r("h1")[0].childNodes[0].nodeValue);

};

script.a.com上的b.html

document.domain 'a.com';

这种方式适用于{www.kuqin.com, kuqin.com, script.kuqin.com, css.kuqin.com}中的任何页面相互通信。

 

备注:

某一页面的domain默认等于window.location.hostname。主域名是不带www的域名,例如a.com,主域名前面带前缀的通常都为二级域名或多级域名,例如www.a.com其实是二级域名。 domain只能设置为主域名,不可以在b.a.com中将domain设置为c.a.com。

问题:

1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。

2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。我们工行商城IM联调过程中尝试过这种方式,最终因为影响页面其他iframe的功能而放弃。最终我们调整了融e购的IM前端结构.



uery下通过jsonp实现跨域

JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。JSON系统开发方法是一种典型的面向数据结构的分析和设计方法,以活动为中心,一连串的活动的顺序组合成一个完整的工作进程。

$.getJSON(url+"?callback=?",    

           function(json){    

       });  

 

$.ajax({    

           url: '',  // 跨域URL   

           type: 'GET',    

           dataType: 'jsonp',    

           jsonp: 'jsoncallback', //默认callback   

           data: mydata, //请求数据   

           timeout: 5000, 

           success: function (json) //客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数    

               if(json.actionErrors.length!=0){    

                   alert(json.actionErrors);    

                  

           },    

           complete: function(XMLHttpRequest, textStatus){    

                 

           }

       });  

3

.服务器端的跨域解决方案

最新的W3C标准里是这么实现HTTP跨域请求的Cross-Origin Resource Sharing跨域的目标服务器要返回一系列的Headers,通过这些Headers来控制是否同意跨域。

Access-Control-Allow-Origin 这个 Header在W3C标准里用来检查该跨域请求是否可以被通过。

从 http://www.a.com/test.html 发起一个跨域请求,请求的地址为: http://www.b.com/test.php 如果 服务器B返回一个如下的header,Access-Control-Allow-Origin: http://www.a.com,那么,这个来自 http://www.a.com/test.html 的跨域请求就会被通过。在这个过程中, request 还会带上这个header,Access-Control-Allow-Origin 的值可以是通配符 *

如果是 的话,就可以接收来自任意source origin的请求。

 

IE8 问题(详情见情景4), 则是通过 XDomainRequest 来实现的这个跨域请求比如类似如下代码就可以实现了:

var request new XDomainRequest();
request.open("GET", xdomainurl);
request.send();

也要求对方服务器返回这个头才行。

服务器端代码的操作示例:

1.我们可以在java代码中加入 response.setHeader("Access-Control-Allow-Origin", "*");

2.html的,须要 

3.“CrossOriginFilter”类,用于支持跨域的 JavaScript 请求,如果您的项目中要支持跨域的服务器推送,可以加入该配置。

package net.icbc.messager.web;

 

import java.io.IOException;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import net.icbc.messager.web.servlet.TokenServlet;

 

import org.apache.log4j.Logger;

 

 

 

public class CrossOriginFilter implements Filter

{

   

 

private static Logger log Logger.getLogger(TokenServlet.class);

// Request headers

    private static final String ORIGIN_HEADER "Origin";

    private static final String ACCESS_CONTROL_REQUEST_METHOD_HEADER "Access-Control-Request-Method";

    private static final String ACCESS_CONTROL_REQUEST_HEADERS_HEADER "Access-Control-Request-Headers";

    // Response headers

    private static final String ACCESS_CONTROL_ALLOW_ORIGIN_HEADER "Access-Control-Allow-Origin";

    private static final String ACCESS_CONTROL_ALLOW_METHODS_HEADER "Access-Control-Allow-Methods";

    private static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER "Access-Control-Allow-Headers";

    private static final String ACCESS_CONTROL_MAX_AGE_HEADER "Access-Control-Max-Age";

    private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER "Access-Control-Allow-Credentials";

    // Implementation constants

    private static final String ALLOWED_ORIGINS_PARAM "allowedOrigins";

    private static final String ALLOWED_METHODS_PARAM "allowedMethods";

    private static final String ALLOWED_HEADERS_PARAM "allowedHeaders";

    private static final String PREFLIGHT_MAX_AGE_PARAM "preflightMaxAge";

    private static final String ALLOWED_CREDENTIALS_PARAM "allowCredentials";

    private static final String ANY_ORIGIN "*";

    private static final List SIMPLE_HTTP_METHODS Arrays.asList("GET", "POST", "HEAD");

 

    private boolean anyOriginAllowed false;

    private List allowedOrigins new ArrayList();

    private List allowedMethods new ArrayList();

    private List allowedHeaders new ArrayList();

    private int preflightMaxAge 0;

    private boolean allowCredentials false;

 

    public void init(FilterConfig config) throws ServletException

    {

        String allowedOriginsConfig config.getInitParameter(ALLOWED_ORIGINS_PARAM);

        if (allowedOriginsConfig == null) allowedOriginsConfig "*";

        String[] allowedOrigins allowedOriginsConfig.split(",");

        for (String allowedOrigin allowedOrigins)

        {

            allowedOrigin allowedOrigin.trim();

            if (allowedOrigin.length() 0)

            {

                if (ANY_ORIGIN.equals(allowedOrigin))

                {

                    anyOriginAllowed true;

                    this.allowedOrigins.clear();

                    break;

                }

                else

                {

                    this.allowedOrigins.add(allowedOrigin);

                }

            }

        }

 

        String allowedMethodsConfig config.getInitParameter(ALLOWED_METHODS_PARAM);

        if (allowedMethodsConfig == null) allowedMethodsConfig "GET,POST";

        allowedMethods.addAll(Arrays.asList(allowedMethodsConfig.split(",")));

 

        String allowedHeadersConfig config.getInitParameter(ALLOWED_HEADERS_PARAM);

        if (allowedHeadersConfig == null) allowedHeadersConfig "X-Requested-With,Content-Type,Accept,Origin";

        allowedHeaders.addAll(Arrays.asList(allowedHeadersConfig.split(",")));

 

        String preflightMaxAgeConfig config.getInitParameter(PREFLIGHT_MAX_AGE_PARAM);

        if (preflightMaxAgeConfig == null) preflightMaxAgeConfig "1800"; // Default is 30 minutes

        try

        {

            preflightMaxAge Integer.parseInt(preflightMaxAgeConfig);

        }

        catch (NumberFormatException x)

        {

            log.info("Cross-origin filter, could not parse '{}' parameter as integer: {}"+ PREFLIGHT_MAX_AGE_PARAM+ preflightMaxAgeConfig);

        }

 

        String allowedCredentialsConfig config.getInitParameter(ALLOWED_CREDENTIALS_PARAM);

        if (allowedCredentialsConfig == null) allowedCredentialsConfig "false";

        allowCredentials Boolean.parseBoolean(allowedCredentialsConfig);

 

        log.debug("Cross-origin filter configuration: +

                  ALLOWED_ORIGINS_PARAM allowedOriginsConfig ", +

                  ALLOWED_METHODS_PARAM allowedMethodsConfig ", +

                  ALLOWED_HEADERS_PARAM allowedHeadersConfig ", +

                  PREFLIGHT_MAX_AGE_PARAM preflightMaxAgeConfig ", +

                  ALLOWED_CREDENTIALS_PARAM allowedCredentialsConfig);

    }

 

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException

    {

        handle((HttpServletRequest)request, (HttpServletResponse)response, chain);

    }

 

    private void handle(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException

    {

        String origin request.getHeader(ORIGIN_HEADER);

        // Is it cross origin request ?

        if (origin != null && isEnabled(request))

        {

            if (originMatches(origin))

            {

                if (isSimpleRequest(request))

                {

                    log.debug("Cross-origin request to {} is simple cross-origin request"+ request.getRequestURI());

                    handleSimpleResponse(request, response, origin);

                }

                else

                {

                    log.debug("Cross-origin request to {} is preflight cross-origin request" +request.getRequestURI());

                    handlePreflightResponse(request, response, origin);

                }

            }

            else

            {

                log.debug("Cross-origin request to request.getRequestURI() with origin origin does not match allowed origins allowedOrigins);

            }

        }

 

        chain.doFilter(request, response);

    }

 

    protected boolean isEnabled(HttpServletRequest request)

    {

        // WebSocket clients such as Chrome implement version of the WebSocket

        // protocol that does not accept extra response headers on the upgrade response

        if ("Upgrade".equalsIgnoreCase(request.getHeader("Connection")) &&

            "WebSocket".equalsIgnoreCase(request.getHeader("Upgrade")))

        {

            return false;

        }

        return true;

    }

 

    private boolean originMatches(String origin)

    {

        if (anyOriginAllowed) return true;

        for (String allowedOrigin allowedOrigins)

        {

            if (allowedOrigin.equals(origin)) return true;

        }

        return false;

    }

 

    private boolean isSimpleRequest(HttpServletRequest request)

    {

        String method request.getMethod();

        if (SIMPLE_HTTP_METHODS.contains(method))

        {

            // TODO: implement better section 6.1

            // Section 6.1 says that for request to be simple, custom request headers must be simple.

            // Here for simplicity just check if there is Access-Control-Request-Method header,

            // which is required for preflight requests

            return request.getHeader(ACCESS_CONTROL_REQUEST_METHOD_HEADER) == null;

        }

        return false;

    }

 

    private void handleSimpleResponse(HttpServletRequest request, HttpServletResponse response, String origin)

    {

        response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);

        if (allowCredentials) response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");

    }

 

    private void handlePreflightResponse(HttpServletRequest request, HttpServletResponse response, String origin)

    {

        // Implementation of section 5.2

 

        // 5.2.3 and 5.2.5

        boolean methodAllowed isMethodAllowed(request);

        if (!methodAllowed) return;

        // 5.2.4 and 5.2.6

        boolean headersAllowed areHeadersAllowed(request);

        if (!headersAllowed) return;

        // 5.2.7

        response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);

        if (allowCredentials) response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");

        // 5.2.8

        if (preflightMaxAge 0) response.setHeader(ACCESS_CONTROL_MAX_AGE_HEADER, String.valueOf(preflightMaxAge));

        // 5.2.9

        response.setHeader(ACCESS_CONTROL_ALLOW_METHODS_HEADER, commify(allowedMethods));

        // 5.2.10

        response.setHeader(ACCESS_CONTROL_ALLOW_HEADERS_HEADER, commify(allowedHeaders));

    }

 

    private boolean isMethodAllowed(HttpServletRequest request)

    {

        String accessControlRequestMethod request.getHeader(ACCESS_CONTROL_REQUEST_METHOD_HEADER);

        log.debug("{} is {}"+ ACCESS_CONTROL_REQUEST_METHOD_HEADER+ accessControlRequestMethod);

        boolean result false;

        if (accessControlRequestMethod != null)

        {

            result allowedMethods.contains(accessControlRequestMethod);

        }

        log.debug("Method {} is" (result "" not") among allowed methods {}"+accessControlRequestMethod+allowedMethods);

        return result;

    }

 

    private boolean areHeadersAllowed(HttpServletRequest request)

    {

        String accessControlRequestHeaders request.getHeader(ACCESS_CONTROL_REQUEST_HEADERS_HEADER);

        log.debug("{} is {}"+ ACCESS_CONTROL_REQUEST_HEADERS_HEADER+  accessControlRequestHeaders);

        boolean result true;

        if (accessControlRequestHeaders != null)

        {

            String[] headers accessControlRequestHeaders.split(",");

            for (String header headers)

            {

                boolean headerAllowed false;

                for (String allowedHeader allowedHeaders)

                {

                    if (header.trim().equalsIgnoreCase(allowedHeader.trim()))

                    {

                        headerAllowed true;

                        break;

                    }

                }

                if (!headerAllowed)

                {

                    result false;

                    break;

                }

            }

        }

        log.debug("Headers [{}] are" (result "" not") among allowed headers {}"+accessControlRequestHeaders+allowedHeaders);

        return result;

    }

 

    private String commify(List strings)

    {

        StringBuilder builder new StringBuilder();

        for (int 0; strings.size(); ++i)

        {

            if (i 0) builder.append(",");

            String string strings.get(i);

            builder.append(string);

        }

        return builder.toString();

    }

 

    public void destroy()

    {

        anyOriginAllowed false;

        allowedOrigins.clear();

        allowedMethods.clear();

        allowedHeaders.clear();

        preflightMaxAge 0;

        allowCredentials false;

    }

}

 

Fileter配置信息:

 

         cross-origin

         net.icbc.messager.web.CrossOriginFilter

     

     

         cross-origin

         /*

     

 

 

4.IE8下的跨域问题

情景4需在情景3的基础上进行,仅仅进行情景3的设置,是在IE8下行不通的.

通过使用 Internet Explorer 中的跨域请求(缩写为“XDR”),开发人员可以创建跨网站数据聚合方案。 这个名为 XDomainRequest 的请求与 XMLHttpRequest 对象类似,但编程模型更加简单,它可以提供一种最简单的方式来向支持 XDR 的第三方站点发出匿名请求,并选择使这些站点的数据可跨域使用。 只需三行代码即可生成基本的跨站点请求。 这将确保针对公共站点(例如,博客或其他社交网络应用程序)的数据聚合简单、安全和快速。

 

下面的 JavaScript 代码介绍 XDomainRequest 对象及其事件、属性和方法。 XDomainRequest 参考页提供了比此处更为详细的信息。

// Creates new XDR object.

xdr new XDomainRequest();  

 

// Indicates there is an error and the request cannot be completed. 

xdr.onerror alert_error;

                        

// The request has reached its timeout. 

xdr.ontimeout alert_timeout;

                        

// The object has started returning data.

xdr.onprogress alert_progress;

                        

// The object is complete. 

xdr.onload alert_loaded;

 

// Sets the timeout interval.

xdr.timeout timeout;

 

// Gets the content-type header in the request.

var content_type xdr.contentType;

 

// Gets the body of the response.

var response xdr.responseText;

                        

// Creates connection with domain's server. 

xdr.open("get", url);

 

// Transmits data string to the server. 

xdr.send();

 

// Terminates pending send.

xdr.abort();

 

附:IE8下的跨域js的兼容设置,实现XDR对象到XHR对象的转换 ieXDRToXHR.js (注此js会对jQuery形成干扰)

 

if (window.XDomainRequest) {

    windows.ieXDRToXHR function(window) {

        "use strict";

        var XHR window.XMLHttpRequest;

 

        window.XMLHttpRequest function() {

            this.onreadystatechange Object;

 

            this.xhr null;

            this.xdr null;

 

            this.readyState 0;

            this.status '';

            this.statusText null;

            this.responseText null;

 

            this.getResponseHeader null;

            this.getAllResponseHeaders null;

 

            this.setRequestHeader null;

 

            this.abort null;

            this.send null;

            this.isxdr false;

 

            // static binding

            var self this;

 

            self.xdrLoadedBinded function() {

                self.xdrLoaded();

            };

            self.xdrErrorBinded function() {

                self.xdrError();

            };

            self.xdrProgressBinded function() {

                self.xdrProgress();

            };

            self.xhrReadyStateChangedBinded function() {

                self.xhrReadyStateChanged();

            };

        };

 

        XMLHttpRequest.prototype.open function(method, url, asynch, user, pwd) {

            //improve CORS deteciton (chat.example.net exemple.net), remove hardcoded http-bind

            var parser document.createElement_x('a');

            parser.href url;

            if (parser.hostname!=document.domain) {

                if (this.xdr === null){

                    this.xdr new window.XDomainRequest();

                }

 

                this.isxdr true;

                this.setXDRActive();

                this.xdr.open(method, url);

            else {

                if (this.xhr === null){

                    this.xhr new XHR();

                }

 

                this.isxdr false;

                this.setXHRActive();

                this.xhr.open(method, url, asynch, user, pwd);

            }

        };

 

        XMLHttpRequest.prototype.xdrGetResponseHeader function(name) {

            if (name === 'Content-Type' && this.xdr.contentType ''){

                return this.xdr.contentType;

            }

 

            return '';

        };

        

        XMLHttpRequest.prototype.xdrGetAllResponseHeaders function() {

            return (this.xdr.contentType '') 'Content-Type: this.xdr.contentType '';

        };

        

        XMLHttpRequest.prototype.xdrSetRequestHeader function(name, value) {

            //throw new Error('Request headers not supported');

        };

        

        XMLHttpRequest.prototype.xdrLoaded function() {

            if (this.onreadystatechange !== null) {

                this.readyState 4;

                this.status 200;

                this.statusText 'OK';

                this.responseText this.xdr.responseText;

                if (window.ActiveXObject){

                    var doc new ActiveXObject('Microsoft.XMLDOM');

                    doc.async='false';

                    doc.loadXML(this.responseText);

                    this.responseXML doc;

                }

                this.onreadystatechange();

            }

        };

        

        XMLHttpRequest.prototype.xdrError function() {

            if (this.onreadystatechange !== null) {

                this.readyState 4;

                this.status 0;

                this.statusText '';

                // ???

                this.responseText '';

                this.onreadystatechange();

            }

        };

        

        XMLHttpRequest.prototype.xdrProgress function() {

            if (this.onreadystatechange !== null && this.status !== 3) {

                this.readyState 3;

                this.status 3;

                this.statusText '';

                this.onreadystatechange();

            }

        };

        

        XMLHttpRequest.prototype.finalXDRRequest function() {

            var xdr this.xdr;

            delete xdr.onload;AZ

            delete xdr.onerror;

            delete xdr.onprogress;

        };

        

        XMLHttpRequest.prototype.sendXDR function(data) {

            var xdr this.xdr;

 

            xdr.onload this.xdrLoadedBinded;

            xdr.onerror this.xdr.ontimeout this.xdrErrorBinded;

            xdr.onprogress this.xdrProgressBinded;

            this.responseText null;

 

            this.xdr.send(data);

        };

        

        XMLHttpRequest.prototype.abortXDR function() {

            this.finalXDRRequest();

            this.xdr.abort();

        };

        

        XMLHttpRequest.prototype.setXDRActive function() {

            this.send this.sendXDR;

            this.abort this.abortXDR;

            this.getResponseHeader this.xdrGetResponseHeader;

            this.getAllResponseHeaders this.xdrGetAllResponseHeaders;

            this.setRequestHeader this.xdrSetRequestHeader;

        };

 

        XMLHttpRequest.prototype.xhrGetResponseHeader function(name) {

            return this.xhr.getResponseHeader(name);

        };

        

        XMLHttpRequest.prototype.xhrGetAllResponseHeaders function() {

            return this.xhr.getAllResponseHeaders();

        };

        

        XMLHttpRequest.prototype.xhrSetRequestHeader function(name, value) {

            return this.xhr.setRequestHeader(name, value);

        };

        

        XMLHttpRequest.prototype.xhrReadyStateChanged function() {

            if (this.onreadystatechange !== null && this.readyState !== this.xhr.readyState) {

                var xhr this.xhr;

 

                this.readyState xhr.readyState;

                if (this.readyState === 4) {

                    this.status xhr.status;

                    this.statusText xhr.statusText;

                    this.responseText xhr.responseText;

                    this.responseXML xhr.responseXML;

                }

 

                this.onreadystatechange();

            }

        };

        

        XMLHttpRequest.prototype.finalXHRRequest function() {

            delete this.xhr.onreadystatechange;

        };

        XMLHttpRequest.prototype.abortXHR function() {

            this.finalXHRRequest();

            this.xhr.abort();

        };

        XMLHttpRequest.prototype.sendXHR function(data) {

            this.xhr.onreadystatechange this.xhrReadyStateChangedBinded;

 

            this.xhr.send(data);

        };

        XMLHttpRequest.prototype.setXHRActive function() {

            this.send this.sendXHR;

            this.abort this.abortXHR;

            this.getResponseHeader this.xhrGetResponseHeader;

            this.getAllResponseHeaders this.xhrGetAllResponseHeaders;

            this.setRequestHeader this.xhrSetRequestHeader;

        };

 

        windows.ieXDRToXHR undefined;

    };

    windows.ieXDRToXHR(window);

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值