• [转]JS跨域总结


    本文转自:http://www.cnblogs.com/qixuejia/archive/2012/08/29/2662220.html

    javascript跨域有两种情况:

    1、基于同一父域的子域之间,如:a.c.com和b.c.com

    2、基于不同的父域之间,如:www.a.com和www.b.com

    3、端口的不同,如:www.a.com:8080www.a.com:8088

    4、协议不同,如:http://www.a.com和https://www.a.com

     

    对于情况3和4,需要通过后台proxy来解决,具体方式如下:

    a、在发起方的域下创建proxy程序

    b、发起方的js调用本域下的proxy程序

    c、proxy将请求发送给接收方并获取相应数据

    d、proxy将获得的数据返回给发起方的js

    发起方页面代码如下:

    <form id="form1" runat="server">
        <div>               
            <input type="text" id="txtSrc" value="http://www.gzsums.edu.cn/webclass/html/html_design.html" style=" 378px" />
            <input id="btnProxy" type="button" value="通过Proxy获取数据" onclick="GetDataFromProxy();" /><br />
            <br />
            <br />
        </div>
       <div id="divData"></div>
    </form>
    </body>
    <script language="javascript"  type="text/javascript">      
       function GetDataFromProxy() {
           var src = document.getElementById('txtSrc').value;
           var request = null;
           if (window.XMLHttpRequest) {
               request = new XMLHttpRequest();
           }
           else if (window.ActiveXObject) {
               request = new ActiveXObject("Microsoft.XMLHTTP");
           }

           request.onreadystatechange = function() {
               var ready = request.readyState;
               var data = null;
               {
                   if (ready == 4) {
                       data = request.responseText;
                       document.getElementById('divData').innerHTML = data;
                   }
                   else {
                       document.getElementById('divData').text = "Loading";
                   }
               }
           }

           var url = "Proxy.ashx?src=" + escape(src);       
           request.open("get",url,false);
           request.send(null);
       }    
    </script>

    发起方Proxy代码如下:

    using System.Data;
    using System.Linq;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using System.Xml.Linq;
    using System.IO;
    using System.Net;
    using System.Text;


    namespace WebApplication1
    {
        /// <summary>
        /// Summary description for $codebehindclassname$
        /// </summary>
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        public class Proxy : IHttpHandler
        {
            const int BUFFER_SIZE = 8 * 1024;
            public void ProcessRequest(HttpContext context)
            {
                context.Response.ContentType = "text/plain";
                string src = context.Request["src"];

                WebRequest wr = WebRequest.Create(src);
                WebResponse wres = wr.GetResponse();
                Encoding resEncoding = System.Text.Encoding.GetEncoding("gb2312");
                StreamReader sr = new StreamReader(wres.GetResponseStream(), resEncoding);
                string html = sr.ReadToEnd();
                sr.Close();
                wres.Close();

                context.Response.Write("<br/><br/><br/><br/>");
                context.Response.Write(html);
            }

            public bool IsReusable
            {
                get
                {
                    return false;
                }
            }
        }
    }

    而情况1和2除了通过后台proxy这种方式外,还可以有7种办法来解决:

    1、document.domain+iframe(只能解决情况1):

        a、在发起方页面和接收方页面设置document.domain,并将值设为父域的主域名(window.location.hostname)

      b、在发起方页面创建一个隐藏的iframe,iframe的源是接收方页面

        c、根据浏览器的不同,通过iframe.contentDocument || iframe.contentWindow.document来获得接收方页面的内容

        d、通过获得的接收方页面的内容来与接收方进行交互

    这种方法有个缺点,就是当一个域被攻击时,另一个域会有安全漏洞出现。

    发起方页面代码如下:

    <body>
    <div>               
        <input type="text" id="txtSrc" value="http://b.a.com/DomainTest2.htm" style=" 378px" />
        <input id="btnDomain" type="button" value="通过Domain获取数据" onclick="GetDataFromDomain();" /><br />
        <br />
        <br />
    </div>
    <div id="divData"></div>
    </body>
    <script language="javascript"  type="text/javascript">
        document.domain = 'a.com';
        var src = document.getElementById('txtSrc').value;
        var ifr = document.createElement('iframe');
        ifr.src = src;
        ifr.style.display = 'none';
        document.body.appendChild(ifr);
        function GetDataFromDomain() {
            var doc = ifr.contentDocument || ifr.contentWindow.document;
            alert(doc.getElementById("data").value);
        }
    </script>

    接收方页面代码如下:

    <body>
        <input type="hidden" id="data" value="Cross Domain" style=" 378px" />
    </body>
    <script language="javascript"  type="text/javascript">
        document.domain = 'a.com';   
    </script>

    2、动态创建script:

      a、在发起方页面动态加载一个script,script的URL指向接收方的一个处理地址(后台),该地址返回的javascript方法会被执行,另外URL中可以传入一些参数,该方法只支持GET方式提交参数。

      b、加载的script可以在调用跨域js方法后再做一些自己的处理

    发起方页面的代码如下:

    <head>
        <title>Script Test</title>
        <script language="javascript" type="text/javascript">
        function load_script(callback){  
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            var src = document.getElementById('txtSrc').value;
            script.type = 'text/javascript';  
            script.src = src;  
            //借鉴了jQuery的script跨域方法  
            script.onload = script.onreadystatechange = function(){  
                if((!this.readyState||this.readyState === "loaded"||this.readyState === "complete")){  
                    callback && callback();  
                    // Handle memory leak in IE  
                    script.onload = script.onreadystatechange = null;  
                    if ( head && script.parentNode ) {  
                      head.removeChild( script );  
                    }  
                }  
            };  
            // Use insertBefore instead of appendChild  to circumvent an IE6 bug.  
            head.insertBefore( script, head.firstChild );
        }   
        </script>
    </head>
    <body>
    <input type="text" id="txtSrc" value="http://www.b.com/scripttest.aspx" style=" 378px" />
    <input type="button" value="通过动态创建script标签来获取数据" onclick="load_script(function(){alert('动态加载script标签成功')})"/>
    </body>

    接收方服务器端代码如下:

    protected void Page_Load(object sender, EventArgs e)
    {           
                Response.Clear();
                Response.ContentType = "application/x-javascript";
                Response.Write(String.Format(@"alert('{0}');", DateTime.Now));
                Response.End();
    }

    3、location.hash+iframe:

      a、发起方创建一个隐藏的iframe,iframe的源指向接收方的页面,并通过接收方页面的hash值来传送数据

      b、发起方创建一个定时器,定时检查自己的location.hash并作相应的处理

        c、接收方创建一个隐藏的iframe,iframe的源指向发起方所在域的一个代理页面,并将接收方根据发起方传入的数据而处理后的数据通过代理页面的hash值来传送

        d、接收方创建一个定时器,定时检查自己的location.hash并作相应的处理

        e、代理页面创建一个定时器,定时检查自己的location.hash并同步更新发起方页面的hash值

    www.a.com/a.html#aaa,其中#aaa就是location.hash值

    发起方页面代码如下:

    <body>
    <div>
    <input type="text" id="txtSrc" value="1" style=" 378px" />
    <input id="btnAddHash" type="button" value="添加Hash值" onclick="addHash();" />
    <iframe id="ifr1" style="display:none"></iframe>
    </div>
    </body>
    <script language="javascript"  type="text/javascript">
        function addHash() {
            var src = document.getElementById('txtSrc').value;
            if (src.length > 0) {           
                changeHash(src);
            }
        }
        function changeHash(src) {
            if (document.getElementById('ifr1')) {
                var ifr = document.getElementById('ifr1');
                ifr.src = 'http://www.b.com/Test/HashTest2.htm#' + src;
            }
            else {
                var ifr = document.createElement('iframe');
                ifr.setAttribute('id', 'ifr1');
                ifr.src = 'http://www.b.com/Test/HashTest2.htm#' + src;
                ifr.style.display = 'none';
                document.body.appendChild(ifr);
            }
        }
        function checkHash() {
            if (location.hash && location.hash.length > 1) {
                changeHash(location.hash.substring(1));
            }
        }
        setInterval(checkHash, 2000);
    </script>

    接收方页面代码如下:

    <body>
    <iframe id="ifr2" style="display:none"></iframe>
    </body>
    <script language="javascript"  type="text/javascript">   
        function checkHash() {
            if (location.hash && location.hash.length > 1) {
                var hashData = location.hash.substring(1);
                var ifr = null;
                if (document.getElementById('ifr2')) {
                    ifr = document.getElementById('ifr2');               
                }
                else {
                    ifr = document.createElement('iframe');
                    ifr.setAttribute('id', 'ifr2');               
                    ifr.style.display = 'none';
                    document.body.appendChild(ifr);
                }
                switch (hashData) {
                    case '1':
                        alert('One');
                        if (ifr) {
                            ifr.src = 'http://www.a.com/test/HashTest3.htm#2';
                        }
                        break;
                    case '2':
                        alert('Two');
                        if (ifr) {
                            ifr.src = 'http://www.a.com/test/HashTest3.htm#1';
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        setInterval(checkHash, 2000);
    </script>

    发起方域下的代理页面代码如下:

    <body></body>
    <script language="javascript" type="text/javascript">
        function checkHash() {       
            if (parent && parent.parent && parent.parent.location && self.location.hash.length > 1) {
                parent.parent.location.hash = self.location.hash.substring(1);
            }
        }
        setInterval(checkHash, 500);
    </script>

    4、window.name:

        a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面

        b、接收方在自己页面通过script将需要传送的数据放入window.name里

        c、发起方在iframe的onload方法里将iframe的源改为和自己在同一个域下的代理页面(因为只能是同一个域下才能访问window.name的值)

        d、获取window.name的值(虽然iframe的源改变了,但是window.name的值不会变)

    window.name的值差不多可以有2MB大小

    发起方页面代码如下:

    <body>
    <div>
    <input id="btnName" type="button" value="通过window.name获取数据" onclick="getData();" />
    <iframe id="ifr1" style="display:none" src="http://www.b.com/Test/NameTest2.htm"></iframe>
    </div>
    </body>
    <script language="javascript"  type="text/javascript">
        var ischanged = false;
        function changeSrc() {
            if (document.getElementById('ifr1')) {
                var ifr = document.getElementById('ifr1');
                if (!ischanged) {
                    ischanged = true;
                    ifr.contentWindow.location = 'http://www.a.com/Test/NameTest3.htm';
                }
                else {
                    var data = ifr.contentWindow.name;
                    alert(data);
                }
            }
            else {
                var ifr = document.createElement('iframe');
                ifr.setAttribute('id', 'ifr1');
                ifr.src = 'http://www.b.com/Test/NameTest2.htm';
                ifr.style.display = 'none';
                document.body.appendChild(ifr);
            }
        }
        function getData() {
            setInterval(changeSrc, 2000);
        }
    </script>

    接收方页面代码如下:

    <body></body>
    <script language="javascript"  type="text/javascript">
        window.name = 'NameTest2';
    </script>

    发起方域下的代理页面代码如下:

    <body></body>

    (其实什么都不用写)

    5、HTML5的postMessage

        a、receiverWindow.postMessage(msg, targetOrigin),receiverWindow就是对接收消息的window的引用,可以是iframe的contentWindow/window.open的返回值/window.frames中的一个;msg就是要发送的消息,string类型;targetOrigin用于限制receiverWindow的URI,包括主域名和端口,使用“*”表示无限制,但是为了安全起见还是需要设置下,以防把消息发送给恶意的网站,如果targetOrigin的URI和receiverWindow的不符,则放弃发送消息。
        b、接收方通过message事件来获得消息,并且通过event.origin的属性来验证发送方并通过event.data来获得传送的消息内容,event.source来获得发送方的window对象
    发起方页面代码如下:
    <body>
    <div>
    <input id="btnPostMessage" type="button" value="通过PostMessage获取数据" onclick="getData();" />
    <iframe id="ifr" style="display:none" src="http://www.b.com/Test/PostMessageTest2.htm"></iframe>
    </div>
    </body>
    <script language="javascript" type="text/javascript">
        function getData() {
            var ifr = document.getElementById('ifr');
            var targetOrigin = 'http://www.b.com';
            if (ifr.contentWindow.postMessage) {
                ifr.contentWindow.postMessage('PostMessageTest2', targetOrigin);
            }
        }
    </script>
    接收方页面代码如下:

    <body></body>
    <script language="javascript" type="text/javascript">
        window.addEventListener('message', function(event) {
            if (event.origin == 'http://www.a.com') {
                alert(event.data);   
                alert(event.source);   
            }
        }, false);
    </script>

    6、window.opener(适用于IE6、7,也就是operner hack方法,不过貌似现在已经不管用了,只要打过微软的安全补丁.kb2497640就不能用了)

      a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面

        b、发起方页面通过iframe.contentWindow.opener = {a: function(params){...}, b: function(params){...} ...}来定义可被接收方调用的方法

        c、接收方页面通过window.opener.a/window.opener.b来调用发起方定义的方法

        d、接收方页面通过parent.opener = {c: function(params){...}, d: function(params){...} ...}来定义可被发起方调用的方法

        e、发起方页面通过opener.c/opener.d来调用接收方定义的方法

    其实原理就是重置opener对象

    发起方页面代码如下:

    <body>
    <iframe id="ifr" src="http://www.b.com/test/OpenerTest2.htm" style="display:none"></iframe>
    </body>
    <script language="javascript"  type="text/javascript">  
            var ifr = document.getElementById('ifr');
            ifr.contentWindow.opener = { a: function(msg) { alert('我调用了a方法获得了消息:' + msg); } }       
    </script>

    接收方页面代码如下:

    <body>
    </body>
    <script language="javascript"  type="text/javascript">
        window.opener.a('aaa');
    </script>

    7、window.navigator(适用于IE6、7,貌似现在还能用,还没被补丁掉)

        a、发起方页面创建一个隐藏的iframe,并且源指向接收方页面

        b、发起方页面通过window.navigator.a = function(params){...}; window.navigator.b = function(params){...}; 来定义被接收方调用的方法

        c、接收方页面通过window.navigator.a(params); window.navigator.b(params);来调用发起方定义的方法

        d、接收方页面通过window.navigator.c = function(params){...}; window.navigator.d = function(params){...}; 来定义被发起方调用的方法

        e、发起方页面通过window.navigator.c(params); window.navigator.d(params);来调用接收方定义的方法

    发起方页面代码如下:

    <body>
    <iframe id="ifr" src="http://www.b.com/test/NavigatorTest2.htm" style="display:none"></iframe>
    </body>
    <script language="javascript"  type="text/javascript">
        window.navigator.a = function(msg) { alert('我调用了a方法获得了消息:' + msg); }
        window.navigator.b = function(msg) { alert('我调用了b方法获得了消息:' + msg); }
        setInterval(function() { window.navigator.c('ccc'); }, 2000);
        setInterval(function() { window.navigator.d('ddd'); }, 2000);
    </script>

    接收方页面代码如下:

    <body>
    </body>
    <script language="javascript"  type="text/javascript">
        window.navigator.c = function(msg) { alert('我调用了c方法获得了消息:' + msg); }
        window.navigator.d = function(msg) { alert('我调用了d方法获得了消息:' + msg); }
        setInterval(function() { window.navigator.a('aaa'); }, 2000);
        setInterval(function() { window.navigator.b('bbb'); }, 2000);
    </script>

    作者:Cat Qi 出处:http://qixuejia.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    [转帖]J2ME程序开发全方位基础讲解汇总
    IWAM账号密码不一致引起IIS无法处理ASP文件
    [存档]J2ME中随机数字处理全攻略
    利用计划任务和VBS脚本实现自动WEB共享文件夹里的文件
    完美解决Java程序在 MOTO E680i 中声音文件播放
    J2ME中使用pauseApp控制手机临时退出JAVA程序
    Web页中使用MediaPlayer
    严重注意MSSQL视图跨数据库复制的问题
    Java下数字类型的转换
    项目开发:电话留言软件(20050717)
  • 原文地址:https://www.cnblogs.com/freeliver54/p/3309565.html
Copyright © 2020-2023  润新知