JS跨域总结

  javascript跨域有两种情况:

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

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

  3、端口的不同,如:www.a.com:8080和www.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="width: 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="width: 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="width: 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="width: 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="width: 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>