系统首页架构经典系列—跨域解决方案汇总
1.什么是跨域?
1.1同源策略
The same-origin policy also applies to XMLHttpRequest and to WebSocket.
1.2 同源策略规则
Compared URL | Outcome | Reason |
---|---|---|
http://www.example.com/dir/page2.html | Success | Same protocol and host |
http://www.example.com/dir2/other.html | Success | Same protocol and host |
http://username:password@www.example.com/dir2/other.html | Success | Same protocol and host |
http://www.example.com:**81**/dir/other.html | Failure | Same protocol and host but different port 端口不同 |
https://www.example.com/dir/other.html | Failure | Different protocol 协议不同 |
http://**en.example.com**/dir/other.html | Failure | Different host 仅主域名相同 |
http://**example.com**/dir/other.html | Failure | Different host (exact match required) |
http://**v2.www.example.com**/dir/other.html | Failure | Different host (exact match required) |
http://www.example.com:**80**/dir/other.html | Depends | Port explicit. Depends on implementation in browser. |
## 2.解决跨域之间的数据交互
2.1 document.domain+iframe
对于主域相同而子域不同的例子,可以通过设置document.domain的办法来解决。举www.a.test.com/a.html和www.b.test.com/b.html为例,只需在a.html中添加一个b.html的iframe,
并且设置两个页面的document.domain都为’a.com’(只能为主域名),两个页面之间即可互相访问了,代码如下:
a.htm可以选择动态生成iframe,也可以选择在a.html用iframe直接引用b页面
I.采用动态创建iframe的形式 a.htm的js中填写
1.<span class="pln">document</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">=</span><span class="str">'a.com'</span><span class="pun">;</span>
2.<span class="kwd">var</span><span class="pln"> ifr </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'iframe'</span><span class="pun">);</span>
3.<span class="pln">ifr</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">'<span style="color: rgb(0, 0, 0); font-family: Arial; line-height: 16.799999237060547px; text-indent: 28px; white-space: normal;">www.b.test.com/b.html</span>'</span><span class="pun">;</span>
4.<span class="pln">ifr</span><span class="pun">.</span><span class="pln">style</span><span class="pun">.</span><span class="pln">display </span><span class="pun">=</span><span class="pln"> </span><span class="str">'none'</span><span class="pun">;</span>
5.<span class="pln">document</span><span class="pun">.</span><span class="pln">body</span><span class="pun">.</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">ifr</span><span class="pun">);</span>
6.<span class="pln">ifr</span><span class="pun">.</span><span class="pln">onload </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(){</span>
7.<span class="pln"> </span><span class="kwd">var</span><span class="pln"> doc </span><span class="pun">=</span><span class="pln"> ifr</span><span class="pun">.</span><span class="pln">contentDocument </span><span class="pun">||</span><span class="pln"> ifr</span><span class="pun">.</span><span class="pln">contentWindow</span><span class="pun">.</span><span class="pln">document</span><span class="pun">;</span>
8.<span class="pln"> </span><span class="com">// 在这里操纵b.html</span>
9.<span class="pln"> alert</span><span class="pun">(</span><span class="pln">doc</span><span class="pun">.</span><span class="pln">getElementById</span><span class="pun">(</span><span class="str">"test"</span><span class="pun">).</span><span class="pln">innerHTML</span><span class="pun">);</span>
10.<span class="pun">};</span>
II采用直接引用iframe的形式 a.htm的内容
1.<span class="dec"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"></span>
2.<span class="tag"><html></span>
3.<span class="tag"><head></span>
4.<span class="tag"><title></title></span>
5.<span class="tag"><script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="tag">></span>
6.<span class="pln"> document</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">=</span><span class="str">'test.com'</span><span class="pun">;</span>
7.<span class="tag"></script></span>
8.<span class="tag"></head></span>
9.<span class="tag"><body></span>
10.<span class="tag"><iframe src ="www.b.test.com/b.html"</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"solrIframe" style ="display:none" </span><span class="tag">></span><span class="tag"></iframe></span>
11.<span class="tag"></body></span>
12.<span class="tag"></html></span>
http://www.c.a.com/b.html
1.<span class="dec"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"></span>
2.<span class="tag"><html></span>
3.<span class="tag"><head></span>
4.<span class="tag"><title></title></span>
5.<span class="tag"><script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="tag">></span>
6.<span class="pln"> document</span><span class="pun">.</span><span class="pln">domain</span><span class="pun">=</span><span class="str">'a.com'</span><span class="pun">;</span>
7.<span class="tag"></script></span>
8.<span class="tag"></head></span>
9.<span class="tag"><body></span>
10.<span class="tag"><h1</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"test"</span><span class="tag">></span><span class="pln">Hello World</span><span class="tag"></h1></span>
11.<span class="tag"></body></span>
12.<span class="tag"></html></span>
如果b.html要访问a.html,可在子窗口(iframe)中通过window.parent来访问父窗口的window对象,然后就可以为所欲为了(window对象都有了,还有啥不行的),同理子窗口也可以和子窗口之间通信。
我们可以通过b.html的XMLHttpRequest来获取数据,再传给a.html,从而解决跨子域获取数据的问题。
2.2 动态脚本注入
浏览器默认禁止了跨域访问,但并不禁止在页面中引用其他域的JS文件,并可以自由执行引入的JS文件中的function(包括操作cookie、Dom等等)。根据这一点,可以方便地通过创建script节点的方法来实现完全
跨域的通信。具体的做法可以参考YUI的Get Utility
这种技术克服了XMLHttpRequest的最大限制,也就是跨域请求数据。直接用JavaScript创建一个新的脚本标签,然后设置它的src属性为不同域的URL。
www.a.com/a.html中的script
1.<span class="kwd">var</span><span class="pln"> dynScript </span><span class="pun">=</span><span class="pln"> document</span><span class="pun">.</span><span class="pln">createElement</span><span class="pun">(</span><span class="str">'script'</span><span class="pun">);</span>
2.<span class="pln">dynScript</span><span class="pun">.</span><span class="pln">src </span><span class="pun">=</span><span class="pln"> </span><span class="str">'http://www.b.com/b.js'</span><span class="pun">;</span>
3.<span class="pln">dynScript</span><span class="pun">.</span><span class="pln">setAttribute</span><span class="pun">(</span><span class="str">"type"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"text/javascript"</span><span class="pun">);</span>
4.<span class="pln">document</span><span class="pun">.</span><span class="pln">getElementsByTagName</span><span class="pun">(</span><span class="str">'head'</span><span class="pun">)[</span><span class="lit">0</span><span class="pun">].</span><span class="pln">appendChild</span><span class="pun">(</span><span class="pln">dynScript</span><span class="pun">);</span>
1.<span class="kwd">function</span><span class="pln"> dynCallback</span><span class="pun">(</span><span class="pln">data</span><span class="pun">){</span>
2.<span class="pln"> </span><span class="com">//处理www.b.com返回的数据 </span>
3.<span class="pln"> alert</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">content</span><span class="pun">);</span>
4.<span class="pun">}</span>
2.3、跨域代理
一种简单的办法,就是把跨域的工作交给服务器,从后台获取其他站点的数据再返回给前台,也就是跨域代理(Cross Domain Proxy)。这段英文比较简单,就不再翻译,请读者自行理解吧
All modern web browsers impose a security restriction on network connections, which includes calls to XMLHttpRequest. This restriction prevents a script or application from making a connection to any web server other than the one the web page originally came from (Internet Explorer will allow cross-domain requests if the option has been enabled in the preferences). If both your web application and the XML data that application uses come directly from the same server, then you do not run into this restriction.
If, however, you serve your web application from one web server and you make web service data requests to another server – for example, to the Yahoo! Web Services – then the browser prevents the connection from being opened at all. Bummer.
There are a number of solutions to this problem but the most commonly-used one is to install a proxy on your web server. Instead of making your XMLHttpRequest calls directly to the web service, you make your calls to your web server proxy. The proxy then passes the call onto the web service and in return passes the data back to your client application. Because the connection is made to your server, and the data comes back from your server, the browser has nothing to complain about.
For security reasons it’s a good idea for any proxy you install on your web server should be limited in use. An open proxy that passes on connections to any web site URL is open to abuse. Although it is difficult to limit the connections to your proxy from only your application, you can prevent the proxy from making connections to servers other than those you specify. Hard code the URL to connect to in the proxy itself or provide limited options. This makes the proxy less open and less useful to users other than your client application.
这种方法似乎蛮简单的,改动也不太大。不过就是http请求多了些,响应慢了些,服务器的负载重了些
2.4 JSONP的形式
JSONP的底层原理实际就是动态创建脚本,利用script标签绕过同源策略,获得一个类似这样的数据,jsonpcallback是页面存在的回调方法,参数就是想得到的json。
按照动态创建脚本的方式,又要插入script标签,又要定义一个回调,略显麻烦,利用jQuery可以直接得到想要的json数据,同样是上面的jsonp:
1.<span class="pln">$</span><span class="pun">(</span><span class="str">"#getJsonpByJquery"</span><span class="pun">).</span><span class="pln">click</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span>
2.<span class="pln"> $</span><span class="pun">.</span><span class="pln">ajax</span><span class="pun">({</span>
3.<span class="pln"> url</span><span class="pun">:</span><span class="pln"> </span><span class="str">'http://localhost:2701/home/somejsonp'</span><span class="pun">,</span>
4.<span class="pln"> dataType</span><span class="pun">:</span><span class="pln"> </span><span class="str">"jsonp"</span><span class="pun">,</span>
5.<span class="pln"> jsonp</span><span class="pun">:</span><span class="pln"> </span><span class="str">"callback"</span><span class="pun">,</span>
6.<span class="pln"> success</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span>
7.<span class="pln"> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span>
8.<span class="pln"> </span><span class="pun">}</span>
9.<span class="pln"> </span><span class="pun">})</span>
10.<span class="pun">})</span>
2.5 使用HTML5 postMessage
HTML5提供了跨文档消息机制(Cross Document Messaging),它提供了跨越frame、tabs或windows通信的能力。
要接收消息的页面需要添加相应的事件监听器,在消息到来时你可以检测其来源来并判断是否处理。
浏览器的支持情况:
检测浏览器支持:
if (typeof window.postMessage === “undefined”) {
// postMessage not supported in this browser
}
发送消息:
发送消息给另一个页面:
window.postMessage(“Hello, world”, “portal.example.com”);
发送消息给iframe:
document.getElementsByTagName(“iframe”)[0].contentWindow.postMessage(“Hello, world”,
“chat.example.net”);
监听消息,首先设置白名单,对接受到的消息进行过滤,只处理信任页面发送的消息。
1.<span class="kwd">var</span><span class="pln"> originWhiteList </span><span class="pun">=</span><span class="pln"> </span><span class="pun">[“</span><span class="pln">portal</span><span class="pun">.</span><span class="pln">example</span><span class="pun">.</span><span class="pln">com</span><span class="pun">”,</span><span class="pln"> </span><span class="pun">“</span><span class="pln">games</span><span class="pun">.</span><span class="pln">example</span><span class="pun">.</span><span class="pln">com</span><span class="pun">”,</span><span class="pln"> </span><span class="pun">“</span><span class="pln">www</span><span class="pun">.</span><span class="pln">example</span><span class="pun">.</span><span class="pln">com</span><span class="pun">”];</span><span class="pln"> </span>
2.<span class="kwd">function</span><span class="pln"> checkWhiteList</span><span class="pun">(</span><span class="pln">origin</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>
3.<span class="pln"> </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">var</span><span class="pln"> i</span><span class="pun">=</span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i</span><span class="pun"><</span><span class="pln">originWhiteList</span><span class="pun">.</span><span class="pln">length</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>
4.<span class="pln"> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">origin </span><span class="pun">===</span><span class="pln"> originWhiteList</span><span class="pun">[</span><span class="pln">i</span><span class="pun">])</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>
5.<span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln"> </span>
6.<span class="pln"> </span><span class="pun">}</span><span class="pln"> </span>
7.<span class="pln"> </span><span class="pun">}</span><span class="pln"> </span>
8.<span class="pln"> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">;</span><span class="pln"> </span>
9.<span class="pun">}</span><span class="pln"> </span>
10.<span class="kwd">function</span><span class="pln"> messageHandler</span><span class="pun">(</span><span class="pln">e</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>
11.<span class="pln"> </span><span class="kwd">if</span><span class="pun">(</span><span class="pln">checkWhiteList</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">origin</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>
12.<span class="pln"> processMessage</span><span class="pun">(</span><span class="pln">e</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln"> </span>
13.<span class="pln"> </span><span class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>
14.<span class="pln"> </span><span class="com">// ignore messages from unrecognized origins </span>
15.<span class="pln"> </span><span class="pun">}</span><span class="pln"> </span>
16.<span class="pun">}</span><span class="pln"> </span>
17.<span class="pln">window</span><span class="pun">.</span><span class="pln">addEventListener</span><span class="pun">(“</span><span class="pln">message</span><span class="pun">”,</span><span class="pln"> messageHandler</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln"> </span>
## 3 在首页展现架构中实际应用
3.1运用嵌套iframe
遇到问题:home在去调用SOLR页面时,SOLR页面页面并没有完全加载,造成调用失败,
应对策略:在SOLR页面添加标志位,待确认加载完全后,home再去调用,代码如下;
home页面中代码如下:
1.<span class="tag"><html></span><span class="pln"> </span>
2.<span class="tag"><head></span><span class="pln"> </span>
3.<span class="tag"><script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="tag">></span>
4.<span class="kwd">var</span><span class="pln"> isChildLoaded</span><span class="pun">=</span><span class="kwd">false</span><span class="pun">;</span><span class="pln"> </span><span class="com">//标志位,判断嵌套的solr页面是否完全加载</span>
5.<span class="pln">displayXmlToPage</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">){</span>
6.<span class="com">//在home页面上展现SOLR返回的数据</span>
7.<span class="pun">}</span>
8.<span class="kwd">if</span><span class="pun">(</span><span class="pln">isChildLoaded</span><span class="pun">){</span>
9.<span class="pln"> getSolrXml</span><span class="pun">(</span><span class="pln">data</span><span class="pun">);</span><span class="com">//执行iframe的上的函数</span>
10.<span class="pun">}</span><span class="kwd">else</span><span class="pun">{</span>
11.<span class="pln"> setTimeOut</span><span class="pun">(</span><span class="pln">getSolrXml</span><span class="pun">,</span><span class="lit">50</span><span class="pun">);</span>
12.<span class="pun">}</span>
13.<span class="tag"></script></span><span class="pln"> </span>
14.<span class="tag"></head></span><span class="pln"> </span>
15.<span class="tag"><body</span><span class="pln"> </span><span class="tag">></span>
16.<span class="tag"><iframe</span><span class="pln"> </span><span class="atn">src</span><span class="pun">=</span><span class="atv">"${solrSearchURL}/searcher/solr.jsp "</span><span class="pln"> </span><span class="atn">id</span><span class="pun">=</span><span class="atv">"solrIframe"</span><span class="pln"> </span><span class="atn">style</span><span class="pun">=</span><span class="atv">"</span><span class="pln">display</span><span class="pun">:</span><span class="pln">none</span><span class="atv">"</span><span class="tag">></iframe</span><span class="pln"> </span><span class="tag">></span>
17.<span class="tag"></body></span><span class="pln"> </span>
18.<span class="tag"></html></span>
solr页面如下:
1.<span class="tag"><html></span><span class="pln"> </span>
2.<span class="tag"><head></span><span class="pln"> </span>
3.<span class="tag"><script</span><span class="pln"> </span><span class="atn">type</span><span class="pun">=</span><span class="atv">"text/javascript"</span><span class="pln"> </span><span class="tag">></span>
4.<span class="com">//获取Solr服务器上的数据</span>
5.<span class="kwd">function</span><span class="pln"> getSolrXml</span><span class="pun">(</span><span class="pln">data</span><span class="pun">){</span><span class="pln"> </span>
6.<span class="pln"> </span><span class="kwd">var</span><span class="pln"> url</span><span class="pun">=</span><span class="str">"http://search.sgcc.com.cn:8088/searcher/SearcherSelectServlet/select"</span><span class="pun">;</span><span class="pln"> </span>
7.<span class="pln"> jQuery</span><span class="pun">.</span><span class="pln">ajax</span><span class="pun">({</span><span class="pln"> </span>
8.<span class="pln"> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">"post"</span><span class="pun">,</span><span class="pln"> </span>
9.<span class="pln"> url</span><span class="pun">:</span><span class="pln"> url</span><span class="pun">,</span><span class="pln"> </span>
10.<span class="pln"> data</span><span class="pun">:</span><span class="pln"> encodeURIComponent</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">queryService</span><span class="pun">)</span><span class="pln"> </span><span class="pun">,</span><span class="pln"> </span>
11.<span class="pln"> async</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"> </span>
12.<span class="pln"> cache</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> </span>
13.<span class="pln"> timeout</span><span class="pun">:</span><span class="pln"> </span><span class="lit">60000</span><span class="pun">,</span><span class="pln"> </span>
14.<span class="pln"> success</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">){</span><span class="pln"> </span>
15.<span class="pln"> parent</span><span class="pun">.</span><span class="pln">displayXmlToPage</span><span class="pun">(</span><span class="pln">resp</span><span class="pun">)</span><span class="pln"> </span><span class="com">//回调主页面的函数,在home页面上进行展现 </span>
16.<span class="pln"> </span><span class="pun">}</span><span class="pln"> </span>
17.<span class="pln"> </span><span class="pun">})</span><span class="pln"> </span>
18.<span class="pun">}</span>
19.<span class="tag"></script></span><span class="pln"> </span>
20.<span class="tag"></head></span><span class="pln"> </span>
21.<span class="tag"><body</span><span class="pln"> </span><span class="atn">onload</span><span class="pun">=</span><span class="atv">"</span><span class="pln">parent</span><span class="pun">.</span><span class="pln">isChildLoaded </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="atv">"</span><span class="tag">></span><span class="pln">测试solr</span><span class="tag"></body></span><span class="pln"> </span>
22.<span class="tag"></html></span>
### 3.2 运用JSONP
1.jsonp的后台action中处理方式:
public void jsonpMethod (){
PrintWriter out=null;
try{
jsonmap = new HashMap();
jsonmap.put(“successed”, true);
String callBack = “”+ callback +”(“+ JSONUtil.serialize(jsonmap) +”)”;
response.setCharacterEncoding(“utf-8”);
response.setContentType(“text/javascript;charset=utf-8”);
response.setHeader(“Cache-Control”, “no-cache”);
out = response.getWriter();
out.write(callBack);
} catch (Exception e) {
throwException(e);
}
}
备注:callback 属于前台传来的参数
jsonp的前台调用形式
var url=http://log.coa.sgcc.com.cn:8006/coa-webapp/json/jsonpMethod .action
jQuery.ajax({
type : “get”, //获取形式
url : url, //请求url
dataType : “jsonp”, //标明是jsonp的形式
jsonpCallback:”callback”,
async : false,
cache : false,
data: {querycond:querycond}, //条件
success : function(json) {
alert(json.list[0].unitName)
}
})
来源: <https://developer.yahoo.com/javascript/howto-proxy.html>
来源: <http://www.2cto.com/kf/201111/110813.html>