雜談——關於“域”和“跨域”那些事
注:本文主要用於自主總結,後期會更新。如果要最全面的,可以參考大神的文章正確面對跨域、別慌
好啦,進入正題。
今天我們來了解一下“跨域”。
究竟什麼是跨域呢?首先我們得先了解一下什麼是域。
一、域是什麼?
在今天很多人都有意識或無意識的跟域這個東西打過交道。如果你在公司裡使用電腦,並且你的電腦接入了公司的區域網,那你的電腦很可能就在一個域中。
如何檢視你的電腦是否連線到一個域中,以Windows為例,右擊我的電腦 –>屬性,可以看到,我現在使用的這臺電腦就加入了一個域。 域已經成為絕大多數公司組織、連線電腦的一種方式。
那麼我們究竟為什麼要使用域?它能給我們帶來什麼好處呢?
假設你是公司的系統管理員,你們公司有一千臺電腦。如果你要為每臺電腦設定登入帳戶,設定許可權(比如是否允許登入帳戶安裝軟體),那你要分別坐在這一千臺電腦前工作。如果你要做一些改變,你也要分別在這一千臺電腦上修改。相信沒有哪個管理員想要用這種不吃不喝不睡覺的方式來工作,所以就應運而生了域的概念。
還以Windows為例,在微軟的世界中,一個域是由一個或多個域控制器(domain controller)來控制的(其實域控制器並不神祕,無非就是裝了一些特別軟體的電腦)。其他的電腦加入該域,就要接受域控制器的控制。域控制器中有兩個重要的表,一個是加入該域的電腦的列表,另一個表用來儲存叫做活動目錄(Active Directory)的東西。活動目錄就是你登入公司網路的帳戶。活動目錄中儲存著你的許可權。
有了域以後,作為管理員,你就可以坐在一臺電腦前,登入到域控制器上,對一切許可權進行控制,而不用跑到每臺電腦前進行設定了。工作效率提高了很多,但是還不夠。假設公司有一千名員工,你是否要在域控制器中對這一千個人分別進行許可權的設定呢?這聽起來也是一件很麻煩的事。其實很多員工的許可權都是相同的,那我們可不可以對這些相同的許可權只設置一次,然後將該許可權分配給相關的員工呢?
答案就是使用分組(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