javascript同源策略和跨域實驗及其跨域解決辦法
二、問題分析: 應用A採用域名http://trade.alibaba.com ,應用B採用的域名 http://56.alibaba.com。屬於相同主域下的不同子域。牽涉出跨域是否能操作其他文件
三、問題延伸: 瞭解跨域,我們先了解一下javascript的同源策略,同源策略阻止從一個源載入的文件或指令碼獲取或設定另一個源載入的文件的屬性。如果它們的協議、埠(如果指明瞭的話)和主機名都相同。則他們屬於同源。深入瞭解同源策略對我們解決問題有很大幫助。
四、實驗目的:
1、證明同源策略在各大瀏覽器下的表現是否統一
2、不同載入方式對同源策略的是否有不同的影響(包括父視窗開啟子視窗、父視窗iframe子視窗、ajax呼叫其他域下的文件載入)
3、探尋解決跨域問題的辦法
五、實驗準備:
1、根據實驗的目的,我們把我們需要做的實驗用列表的方式展現出來,方便得出結論。結論填入下表
1 | 型別 | 頁面A | 頁面B | 頁面關係和瀏覽器表現 (N為出錯,Y為正常) |
||||||||
open | iframe | ajax | ||||||||||
ie | ff | g | ie | ff | g | ie | ff | g | ||||
2 | 協議、埠、域均相同 |
http://www.alibaba.com/ domain1/a.htm |
http://www.alibaba.com/ domain2/b.htm |
Y | Y | Y | Y | Y | Y | Y | Y | Y |
3 | 協議、埠、域均相同 |
http://www.alibaba.com:8080/ domain1/a.htm |
http://www.alibaba.com:8080/ domain2/b.htm |
Y | Y | Y | Y | Y | Y | Y | Y | Y |
4 | 協議不同 |
http://www.alibaba.com/ domain1/a.htm |
https://www.alibaba.com/ domain2/b.htm |
|||||||||
5 | 埠不同 |
http://www.alibaba.com/ domain1/a.htm |
http://www.alibaba.com:8080/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
6 | 域不同 |
http://www.alibaba.com/ domain1/a.htm |
http://www.alisoft.com/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
7 | 子域(主機名)不同 |
http://56.alibaba.com/ domain1/a.htm |
http://trade.alibaba.com/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
8 | 子域(主機名)不同,子域為空 |
http://alibaba.com/ domain1/a.htm |
http://56.alibaba.com/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
9 |
子域不同設定 document.domain ="alibaba.com" 屬性後 |
http://56.alibaba.com/ domain1/a.htm |
http://trade.alibaba.com/ domain1/a.htm |
Y | Y | Y | Y | Y | Y | N | N | N |
10 | 域不同,IP相同 |
http://56.alibaba.com/ domain1/a.htm |
http://127.0.0.1/ domain2/b.htm |
N | N | N | N | N | N | N | N | N |
通過上面的表格,能確定同源策略在各個瀏覽器的支援程度,並檢驗各種頁面引入方式對同源策略的影響。
2、其中A頁面和B頁面關係三種情況如下圖:
圖2-1 window.open開啟子視窗
其中A頁面原始碼如下:
- <html>
- <head>
- <title>Page A</title>
- <script>
- alert("domain is:"+document.domain);
- var openerWindow=function(url){
- window.open(url);
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">A</h1>
- <div style="text-align:center"><input type="text" id="mytext" value="I'm page A"/><br/>
- <button onclick="openerWindow('http://www.alibaba.com/domain2/b.htm')">開啟子視窗</button><!--url根據跨域更改-->
- </div>
- </body>
- </html>
其中B頁面原始碼如下:
- <html>
- <head>
- <title>Page B</title>
- <script>
- alert("domain is:"+document.domain);
- var refleshOpener=function(){
- window.opener.location.reload();
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">B</h1>
- <div style="text-align:center"><input type="text" id="mytext" value="I'm page B"/><br/>
- <button onclick="refleshOpener()">重新整理父視窗</button>
- </div>
- </body>
- </html>
圖2-2 iframe引入子視窗
其中A頁面原始碼如下:
- <html>
- <head>
- <title>Page A</title>
- <script>
- alert("domain is:"+document.domain);
- var getValue=function(){
- alert("子視窗中值為:"+window.frames['little_frame'].document.getElementById('mytext').value);
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">A</h1>
- <div style="text-align:center">
- <iframe id="little_frame" name="little_frame" width="300" height="300" src="http://www.alibaba.com/domain2/b.htm"></iframe><br/>
- <input type="text" id="mytext" value="I'm page A"/><br/>
- <button onclick="getValue()">獲取子視窗屬性</button>
- </div>
- </body>
- </html>
其中B頁面原始碼如下:
- <html>
- <head>
- <title>Page B</title>
- <script>
- alert("domain is:"+document.domain);
- var getValue=function(){
- alert("父視窗中值為:"+top.document.getElementById('mytext').value);
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">B</h1>
- <div style="text-align:center"><input type="text" id="mytext" value="I'm page B"/><br/>
- <button onclick="getValue()">獲取父視窗屬性</button>
- </div>
- </body>
- </html>
圖2-3 ajax的load方法載入B.htm的內容
其中A頁面原始碼如下:
- <html>
- <head>
- <title>Page A</title>
- <script src="jquery-1.3.2.js"></script>
- <script>
- alert("domain is:"+document.domain);
- var loadB=function(){
- $('#loadContentDiv').load("http://www.alibaba.com/domain2/b_ajax.htm");
- }
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">A</h1>
- <div style="text-align:center">
- <input type="text" id="mytext" value="I'm page A"/><br/><br/>
- <div id="loadContentDiv">這裡載入Page B</div>
- <button onclick="loadB()">Ajax載入Page B</button>
- </div>
- </body>
- </html>
其中B頁面原始碼如下:
Page b程式碼- <html>
- <head>
- <title>Page B</title>
- <script>
- </script>
- </head>
- <body>
- <h1 style="text-align:center;font-size:100px;">B</h1>
- </body>
- </html>
3、伺服器和頁面部署:
頁面位置如下:
伺服器採用tomcat,在tomcat安裝目錄下conf\Catalina\localhost\ 新建domain1.xml、domain2.xml
配置分別如下:
- <?xml version="1.0" encoding="UTF-8"?> <Context docBase="D:/experiment/domain1" path="domain1" reloadable="true"></Context>
- <?xml version="1.0" encoding="UTF-8"?> <Context docBase="D:/experiment/domain2"
- path="domain2" reloadable="true"></Context>
4、通過host繫結來模擬跨域請求。
通過修改C:/windows/system32/drivers/etc/hosts檔案
如在測試第5組時的繫結為:
127.0.0.1 www.alibaba.com
127.0.0.1 www.alisoft.com
六、實驗過程
實驗過程有些枯燥,對於協議或埠不同的情況,需要在本地架設兩套伺服器。
其中需要值得說明的是子域的情況:
在驗證http://alibaba.com/domain1/a.htm和http://56.alibaba.com/domain2/b.htm的跨域的跨域過程中,有點令人費解的地方。即,頁面a.htm不設定document.domain的值,預設為alibaba.com,在b.htm中設定document.domain='alibaba.com'。按道理應該可以繞過跨域的問題,但是這個時候,發現跨域的問題任然存在。於是在a.html中寫入 document.domain=document.domain之後,跨域問題就解決了。是不是空的子域跟主域瀏覽器還是認為有差別呢。
七、實驗結論
通過實驗,我們可以得出結論
1、主要瀏覽器對javascript的同源特性的支援良好,不存在有的瀏覽器不行的情況。
2、對於不同的載入關係,包括open、iframe、ajax方式,也都表現統一。但是ajax載入的時候,在設定document.domain過程中,由於js在載入進來的時候未執行,所以被load頁面的document.domain沒有生效,造成該方式不能解決ajax的子域跨域問題。但是能用去其他兩種開啟關係解決子域的跨域。
八、跨域解決辦法探討
1、子域的跨域問題,上面的實驗已經解決了兩中情況,但是ajax載入的情況,還是得不到解決
2、我們經常引用其他站點的js,比如YUI的庫,可以直接在yahoo網站上引用進來,並可以在本頁面得到執行。也就是說<script>標籤提供跨域特性,那麼我們在頁面A寫入<script src='http://www.domain2.com/getData.jsp'></script>,那麼該段js會跨域載入domain2下getData.jsp輸出的內容。但是需要保證該輸出為js允許的格式,比如是json串。這也就是jsonp的原理了。主要用於ajax跨域請求。但是對於操作跨域開啟的視窗和iframe包含的視窗中的內容時,該方法也是力不從心。
3、我們開啟一個頁面或者嵌入iframe,再或者寫入一個連線,經常會用到錨點這個概念。比如<a href='http://www.alibaba.com/b.htm#section1'>第一章節</a>這個#號後面跟的便是錨點的位置,用於頁面級的定位。在js裡面可以通過hash讀取改值。ok,在跨域開啟頁面或者iframe頁面的時候,我們也可以通過該方式寫入這個hash值,完成跨域頁面間的通訊。但是隻能父視窗跟子視窗傳值,如果子視窗還需要傳給父視窗值,那麼還需要在子視窗中嵌入iframe,該iframe跟最上面的父視窗同域。這樣就可以完成跨域頁面的雙向通訊了。但是相互操作頁面dom還是不可行的。如果有頁面間的通訊機制,其實很多跨域的問題就已經得到解決了。
當然,跨域的解決途徑不僅僅只有上面提到的這三種方式。或者你可以也可以想想什麼標籤的什麼特性可以用於跨域呢。