1. 程式人生 > >雜談——關於“域”和“跨域”那些事

雜談——關於“域”和“跨域”那些事

注:本文主要用於自主總結,後期會更新。如果要最全面的,可以參考大神的文章正確面對跨域、別慌

好啦,進入正題。

今天我們來了解一下“跨域”。

究竟什麼是跨域呢?首先我們得先了解一下什麼是域。

一、域是什麼?

在今天很多人都有意識或無意識的跟域這個東西打過交道。如果你在公司裡使用電腦,並且你的電腦接入了公司的區域網,那你的電腦很可能就在一個域中。

如何檢視你的電腦是否連線到一個域中,以Windows為例,右擊我的電腦 –>屬性,可以看到,我現在使用的這臺電腦就加入了一個域。 域已經成為絕大多數公司組織、連線電腦的一種方式。

那麼我們究竟為什麼要使用域?它能給我們帶來什麼好處呢?

假設你是公司的系統管理員,你們公司有一千臺電腦。如果你要為每臺電腦設定登入帳戶,設定許可權(比如是否允許登入帳戶安裝軟體),那你要分別坐在這一千臺電腦前工作。如果你要做一些改變,你也要分別在這一千臺電腦上修改。相信沒有哪個管理員想要用這種不吃不喝不睡覺的方式來工作,所以就應運而生了域的概念。

 還以Windows為例,在微軟的世界中,一個域是由一個或多個域控制器(domain controller)來控制的(其實域控制器並不神祕,無非就是裝了一些特別軟體的電腦)。其他的電腦加入該域,就要接受域控制器的控制。域控制器中有兩個重要的表,一個是加入該域的電腦的列表,另一個表用來儲存叫做活動目錄(Active Directory)的東西。活動目錄就是你登入公司網路的帳戶。活動目錄中儲存著你的許可權。

你在某臺電腦上登入,你鍵入使用者名稱和密碼,你的電腦首先要把你的登入資訊傳送到域控制器,域控制器首先核實你的登入資訊是否正確,然後把一個叫access key的東西返還給你登入的電腦。這個access key中就包含著你的許可權,由它來決定你是否可以安裝軟體,或者使用印表機等等。

有了域以後,作為管理員,你就可以坐在一臺電腦前,登入到域控制器上,對一切許可權進行控制,而不用跑到每臺電腦前進行設定了。工作效率提高了很多,但是還不夠。假設公司有一千名員工,你是否要在域控制器中對這一千個人分別進行許可權的設定呢?這聽起來也是一件很麻煩的事。其實很多員工的許可權都是相同的,那我們可不可以對這些相同的許可權只設置一次,然後將該許可權分配給相關的員工呢?

答案就是使用分組(Group)。比如在學校裡,我們設定學生組和教師組。在某臺電腦上我們設定一個共享資料夾,學生組的許可權是不可訪問,而教師組可以對該資料夾進行訪問。我們將不同的使用者放入不同的分組裡,然後對組進行許可權設定,這樣就免去了我們要對每個使用者進行設定的麻煩。比組更大的單位是組織單位(Organization Unit),一個組織單位可以包含使用者,組,資源(電腦,印表機等),還可以包含其他組織單位。

舉個簡單的例子,有一個商場中的電腦加入了該商場的域,但是該電腦放置在公共場合,有很大的風險,所以我們可以將該電腦放置在一個單獨的組織單位中,然後對該組織單位進行許可權設定,比如不論誰在該組織單位中登入,都不可以修改他的密碼。在很多的實際情況中,一個公司又有下面的子公司,所以就造成母公司有一個域,而子公司也有一個單獨的域。母公司的域與子公司的域如何聯絡起來呢?我們可以在它們之間建立一種叫信任(Trust)的關係。如果母公司的帳戶想要能夠登入到子公司的域中,子公司的域就要對母公司的域建立信任關係。當母公司域的帳戶想要登入到子公司域中時,子公司域由於信任母公司的域,所以它會聽從從母公司域的域控制器返回的access key。反過來,由於母公司的域沒有建立對子公司的信任,所以如果子公司的帳戶想要登入到母公司的域中是不可能的。

注:雖然我們上面的例子都是Windows的域,但域這個概念絕不是微軟獨創的。你也可以在Linux中使用一個叫Samba的軟體來建立域。如果你要用Windows搭建一個域環境的話,要注意兩點:

(1) 加入域不能使用Home版的Windows作業系統(顧名思義它是給你在家用的,而你家裡是不用搭建域的)。

(2) 域控制器不能使用web edition server,因為它沒有安裝活動目錄。 在現實環境中,有很多公司在域中使用多個域控制器,因為如果只使用一個域控制器的話,一旦域控制器不能正常工作,整個域就會癱瘓。在Samba中,Linux使用兩個域控制器,一個是主域控制器,一個是備份域控制器。一旦主域控制器發生故障,備份域控制器就開始工作,直到主域控制器恢復正常。備份域控制器就是一個主域控制器的拷貝,但是它的資料都是隻讀的,也就是說管理員不能在備份域控制器上修改許可權。

二、什麼是域名

域,就像是一座城,那有了城,這座城當然就得有名字啦,我們總不能一直叫“那座城”吧。

域名呢,就是你在這個體系中的名稱或地址。

可能有人對於域名有過了解,我們先回顧一下域名地址的組成:
http:// (協議號)
www (子域名)
google (主域名)
8080 (埠號)
script/jquery.js (請求的地址)
* 當協議、子域名、主域名、埠號都相同時,屬於同一個“域”。

三、什麼是跨域

從上文我們可以知道,一個域名包含協議號、子域名、主域名、埠號以及請求的地址。當協議、子域名、主域名、埠號都相同時,屬於同一個“域”。

既然有同“域”,那當然也有跨域了。那什麼叫跨域呢?

當協議、子域名、主域名、埠號中任意一各不相同時,都算不同的“域”。 不同的域之間相互請求資源,就叫“跨域”。

也許光說的話大家不好理解,那麼正好拾人牙慧,給大家舉些例子。

http://www.111.com/index.html 呼叫 http://www.111.com/server.php (非跨域)

http://www.111.com/index.html 呼叫 http://www.222.com/server.php (主域名不同:123/456,跨域)

http://abc.111.com/index.html 呼叫 http://def.111.com/server.php (子域名不同:abc/def,跨域)

http://www.111.com:8080/index.html 呼叫 http://www.111.com:8081/server.php (埠不同:8080/8081,跨域)

http://www.111.com/index.html 呼叫 https://www.111.com/server.php (協議不同:http/https,跨域)

需要注意一點:localhost和127.0.0.1雖然都指向本機,但也屬於跨域。

看完上面的例子,大家想必就明白什麼叫跨域了。

四、如何跨域?

 

跨域,是指瀏覽器不能執行其他網站的指令碼。它是由瀏覽器的同源策略造成的,是瀏覽器對JavaScript實施的安全限制。

同源策略限制了一下行為:

  • Cookie、LocalStorage 和 IndexDB 無法讀取
  • DOM 和 JS 物件無法獲取
  • Ajax請求傳送不出去

瀏覽器執行javascript指令碼時,會檢查這個指令碼屬於哪個頁面,如果不是同源頁面,就不會被執行。

那我要跨域訪問的話,又該如何呢?

解決辦法:

1、JSONP:

jsonp 全稱是JSON with Padding,是為了解決跨域請求資源而產生的解決方案,是一種依靠開發人員創造出的一種非官方跨域資料互動協議。

一個是描述資訊的格式,一個是資訊傳遞雙方約定的方法。

jsonp的產生:

1.AJAX直接請求普通檔案存在跨域無許可權訪問的問題,不管是靜態頁面也好.

2.不過我們在呼叫js檔案的時候又不受跨域影響,比如引入jquery框架的,或者是呼叫相片的時候

3.凡是擁有scr這個屬性的標籤都可以跨域例如<script><img><iframe>

4.如果想通過純web端跨域訪問資料只有一種可能,那就是把遠端伺服器上的資料裝進js格式的檔案裡.

5.而json又是一個輕量級的資料格式,還被js原生支援

6.為了便於客戶端使用資料,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許使用者傳遞一個callback 引數給服務端,

要注意JSONP只支援GET請求,不支援POST請求。

 

demo1:基於script標籤實現跨域

舉個例子:我在http://study.cn/json/jsonp/jsonp_2.html下請求一個遠端的js檔案


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script type="text/javascript">
    var message = function(data) {
        alert(data[1].title);
    };
</script>

<script type="text/javascript" src="http://web.cn/js/message.js"></script>
</head>
<body>
<div id='testdiv'></div>
</body>
</html>

 

遠端的message.js檔案是

message([
     {"id":"1", "title":"天津新聞聯播,雷人搞笑的男主持人"},
     {"id":"2", "title":"樓市告別富得流油 專家:房價下跌是大概率事件"},
     {"id":"3", "title":"法國人關注時事 八成年輕人每天閱讀新聞"},
     {"id":"4", "title":"新聞中的歷史,歷史中的新聞"},
     {"id":"5", "title":"東陽新聞20140222"},
     {"id":"6", "title":"23個職能部門要增加新聞釋出頻次"},
     {"id":"7", "title":"《貴州新聞聯播》 中國美麗鄉村"},
     {"id":"8", "title":"朝韓離散家屬團聚首輪活動結束"},
     {"id":"9", "title":"索契冬奧會一天曝出兩例興奮劑事件"},
     {"id":"10", "title":"今天中國多地仍將出現中度霾"}
 ]);

 

這個時候我們得到的相應頭是:

這樣就實現跨域成功了,因為服務端返回資料時會將這個callback引數(message)作為函式名來包裹住JSON資料,這樣客戶端就可以隨意定製自己的函式來自動處理返回資料了。

demo2: 基於script標籤實現跨域

讓遠端js知道它應該呼叫的本地函式叫什麼名字,只要服務端提供的js指令碼是動態生成的就好了,這樣前臺只需要傳一個callback引數過去告訴服務端,我需要XXX程式碼,於是服務端就會得到相應了.

例如 在http://study.cn/json/jsonp/jsonp_3.html頁面請求 http://192.168.31.137/train/test/jsonpthree

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script type="text/javascript">
    var messagetow = function(data){
        alert(data);
    };
    var url = "http://192.168.31.137/train/test/jsonpthree?callback=messagetow";
    var script = document.createElement('script'); 
    script.setAttribute('src', url); 
    document.getElementsByTagName('head')[0].appendChild(script);
</script>
</head>
<body>
</body>
</html>

得到的響應頭是:

demo3:  基於jquery跨域

那麼如何用jquery來實現我們的跨域呢???jquery已經把跨域封裝到ajax上了,而且封裝得非常的好,使用起來也特別方便

如果是一般的ajax請求:

$.ajax({
        url:'http://192.168.31.137/train/test/testjsonp',
        type : 'get',
        dataType : 'text',
        success:function(data){
            alert(data);
        },
        error:function(data){
            alert(2);
        }        
    });​​​​​​​

 

那麼在瀏覽器中會報錯:

 

jsonp形式的ajax請求:並且通過get請求的方式傳入引數,注意:跨域請求是隻能是get請求不能使用post請求

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="./js/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
    var name = 'chenshishuo';
    var sex = 'man';
    var address = 'shenzhen';
    var looks = 'handsome ';
     $.ajax({
         type : 'get',
         url:'http://192.168.31.137/train/test/testjsonp',
        data : {
            name : name,
            sex : sex,
            address : address,
            looks : looks,
        },
        cache :false,
        jsonp: "callback",
        jsonpCallback:"success",
        dataType : 'jsonp',
        success:function(data){
            alert(data);
        },
        error:function(data){
            alert('error');
        }        
    });
});
</script>
</head>
<body>
<input id='inputtest' value='546' name='inputtest'>
<div id='testdiv'></div>
</body>
</html>

 

jsonp 傳遞給請求處理程式或頁面的,用以獲得jsonp回撥函式名的引數名(預設為:callback)
jsonpCallback 自定義的jsonp回撥函式名稱,預設為jQuery自動生成的隨機函式名

看看請求頭和相應頭吧

請求頭:jquery會自動帶入callback引數,當服務端獲取到這個引數後,返回回來.(響應頭)

 

2、代理

例如www.123.com/index.html需要呼叫www.456.com/server.php,可以寫一個介面www.123.com/server.php,由這個介面在後端去呼叫www.456.com/server.php並拿到返回值,然後再返回給index.html,這就是一個代理的模式。相當於繞過了瀏覽器端,自然就不存在跨域問題。

用的大多數的方式就是用ifram代理。

如果你想在http://a.study.cn/a.html頁面中通過ajax直接請求頁面http://b.study.cn/b.html,即使你設定了相同的document.domain也還是不行的.

所以修改document.domain的方法只適用於不同子域的框架(父類與子類)間的互動。

如果想通過使用ajax的方法去與不同子域間的資料互動或者是js呼叫,只有兩種方法,一種是使用jsonp的方法外,還有一種是使用iframe來做一個代理。

原理就是讓這個 iframe載入一個與你想要通過ajax獲取資料的目標頁面處在相同的域的頁面,所以這個iframe中的頁面是可以正常使用ajax去獲取你要的資料 的,

然後就是通過我們剛剛講得修改document.domain的方法,讓我們能通過js完全控制這個iframe,這樣我們就可以讓iframe去發 送ajax請求,然後收到的資料我們也可以獲得了。

下面是一個例子:

demo4: 通過iframe來跨子域

http://a.study.cn/a.html 請求 http://b.study.cn/b.html

在a.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
        <script type="text/javascript">
            document.domain = 'study.cn';
            function test() {
                alert(document.getElementById('a').contentWindow);
            }
        </script>
</head>
<body>
    <iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
</html>

 

 在b.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>

<script type="text/javascript">
document.domain = 'study.cn';
</script>
</head>
<body>
    我是b.study.cn的body
</body>
</html>

 

 

 執行效果截圖:

我們就可以通過js訪問到iframe中的各種屬性和物件了

這裡需要注意的是:

基於iframe實現的跨域要求兩個域具有aa.xx.com,bb.xx.com 這種特點,

也就是兩個頁面必須屬於一個基礎域(例如都是xxx.com),使用同一協議和同一埠,這樣在兩個頁面中同時新增document.domain,就可以實現父頁面呼叫子頁面的函式

要點就是 :通過修改document.domain來跨子域

 

3、PHP端修改header(XHR2方式)

在php介面指令碼中加入以下兩句即可:

header('Access-Control-Allow-Origin:*');//允許所有來源訪問

header('Access-Control-Allow-Method:POST,GET');//允許訪問的方式
 

 

 

好啦,以上就是關於域的一些總結。大家如果對於上文總結的內容有自己的看法,歡迎留言評論,我們一起交流交流呀~~

 

 

本文參考文章:

https://www.cnblogs.com/chenshishuo/p/4919224.html?tdsourcetag=s_pcqq_aiomsg

https://blog.csdn.net/lambert310/article/details/51683775?tdsourcetag=s_pcqq_aiomsg