同源策略的基本概念
1995年,同源政策由 Netscape 公司引入瀏覽器。目前,所有瀏覽器都實行這個政策。同源策略:最初,它的含義是指,A網頁設置的 Cookie,B網頁不能打開,除非這兩個網頁"同源"。所謂"同源"指的是"三個相同"。
協議相同
域名相同
端口相同
舉例來說,這個網址http://www.example.com/dir/page.html
協議是http://
,
域名是www.example.com
,端口是80
(默認端口可以省略)。它的同源情況如下。
http://www.example.com/dir2/other.html:同源
file:///F:/phpStudy/WWW/day01/04-demo/04.html 不同源(協議不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
同源策略的目的
同源政策的目的,是為了保證用戶信息的安全,防止惡意的網站竊取數據。
同源策略的限制范圍
隨着互聯網的發展,“同源策略”越來越嚴格,目前,如果非同源,以下三種行為都將收到限制。
1. Cookie、LocalStorage 和 IndexDB 無法讀取。
2. DOM 無法獲得。
3. AJAX 請求在瀏覽器端有跨域限制
雖然這些限制是很有必要的,但是也給我們日常開發帶來不好的影響。比如實際開發過程中,往往都會把服務器端架設到一台甚至是一個集群的服務器中,把客戶端頁面放到另外一個單獨的服務器。那么這時候就會出現不同源的情況,如果我們知道兩個網站都是安全的話,我們是希望兩個不同源的網站之間可以相互請求數據的。這就需要使用到跨域 。
跨域
jsonp( 無兼容性問題 )
JSONP(JSON with Padding)、可用於解決主流瀏覽器的跨域數據訪問的問題。
原理:服務端返回一個定義好的js函數的調用,並且將服務器的數據以該函數參數的形式傳遞過來,這個方法需要前后端配合
script
標簽是不受同源策略的限制的,它可以載入任意地方的 JavaScript 文件。類似的還有img
和link
標簽
<!--不受同源策略限制的標簽-->
<img src="http://www.api.com/1.jpg" alt="">
<link rel="stylesheet" href="http://www.api.com/1.css">
<script src="http://www.api.com/1.js"></script>
jsonp演化過程1
php文件
header("content-type:text/html;charset=utf-8");
echo "alert(1111)";
html文件
<script src="http://www.api.com/testjs.php"></script>
原理:其實src的路徑是什么文件不重要,無論引入js文件還是php文件,最后返回給瀏覽器的都是字符串,因此我們script標簽是可以引入一個php文件的。
jsonp演化過程2
php文件
header("content-type:text/html;charset=utf-8");
echo "var a = 118;";
html文件
<script src="http://www.api.com/testjs.php"></script>
<script>
//a打印出來了118
console.log(a);
</script>
我們現在做到了一件事情,從不同源的php文件中獲取到了數據
缺點:獲取數據的script標簽必須寫在使用的script標簽的前面,必須保證先有數據才能對數據進行渲染。
jsonp演化過程3
php代碼
header("content-type:text/html;charset=utf-8");
$arr = array(
"name"=>"zs",
"age"=>18
);
$result = json_encode($arr);
//這是一段js函數的調用的代碼,$result就是我們想要的數據
echo "func($result)";
js代碼
<script>
function func(data) {
console.log(data);
}
</script>
<script src="http://www.api.com/testjs.php"></script>
缺點:后端必須知道前端聲明的方法的名字,后端才能調用。
jsonp演化過程4
php代碼
header("content-type:text/html;charset=utf-8");
$arr = array(
"name"=>"zs",
"age"=>18
);
$result = json_encode($arr);
//這是一段js函數的調用的代碼,$result就是我們想要的數據
echo $_GET['callback']."($result)";
javascript代碼
function fun(data) {
console.log(data);
}
var button = document.querySelector("button");
button.onclick = function () {
var script = document.createElement("script");
script.src = "http://www.api.com/testjs.php?callback=fun";
document.body.appendChild(script);
}
-
說白了,jsonp的原理就是 借助了script標簽 src 請求資源時, 不受同源策略的限制.
-
在服務端返回一個函數的調用,將數據當前調用函數的實參。
-
在瀏覽器端,需要程序要聲明一個全局函數,通過形參就可以獲取到服務端返回的對應的值
jsonp原理大家需要知道,但不用太過於去糾結這個原理,因為jquery已經幫我們封裝好了,我們使用起來非常的方便。
jquery對於jsonp的封裝
//使用起來相當的簡單,跟普通的get請求沒有任何的區別,只需要把dataType固定成jsonp即可。
$.ajax({
type:"get",
url:"http://www.Jepson.com/testjs.php",
dataType:"jsonp",
data:{
uname:"Jepson",
upass:"123456"
},
success:function (info) {
console.log(info);
}
});
XMLHttpRequest2.0
XMLHttpRequest是一個javascript內置對象,使得Javascript可以進行異步的HTTP通信。2008年2月,就提出了XMLHttpRequest Level 2 草案。
老版本的XMLHttpRequest的缺點:
1. 僅支持傳輸文本數據,無法傳說二進制文件,比如圖片視頻等。
2. 傳輸數據時,沒有進度信息,只能提示完成與否。
3. 受到了"同源策略"的限制
新版本的功能:
1. 可以設置timeout超時時間
2. 可以使用formData對象管理表單數據
3. 允許請求不同域名下的數據(跨域)
4. 支持上傳二進制文件
5. 可以獲取數據傳輸的進度信息
timeout設置超時
xhr.timeout = 3000;//設置超時時間
xhr.ontimeout = function(){
alert("請求超時");
}
formData管理表單數據
formData對象類似於jquery的serialize方法,用於管理表單數據
使用特點:
1. 實例化一個formData對象, new formData(form); form就是表單元素
2. formData對象可以直接作為 xhr.send(formData)的參數。注意此時數據是以二進制的形式進行傳輸。
3. formData有一個append方法,可以添加參數。formData.append("id", "1111");
4. 這種方式只能以post形式傳遞,不需要設置請求頭,瀏覽器會自動為我們設置一個合適的請求頭。
代碼示例:
//1. 使用formData必須發送post請求
xhr.open("post", "02-formData.php");
//2. 獲取表單元素
var form = document.querySelector("#myForm");
//3. 創建form對象,可以直接作為send的參數。
var formData = new FormData(form);
//4. formData可以使用append方法添加參數
formData.append("id", "1111");
//5. 發送,不需要指定請求頭,瀏覽器會自動選擇合適的請求頭
xhr.send(formData);
文件上傳
以前,文件上傳需要借助表單進行上傳,但是表單上傳是同步的,也就是說文件上傳時,頁面需要提交和刷新,用戶體驗不友好,xhr2.0中的formData對象支持文件的異步上傳。
var formData = new FormData();
//獲取上傳的文件,傳遞到后端
var file = document.getElementById("file").files[0];
formData.append("file", file);
xhr.send(formData);
顯示文件進度信息
xhr2.0還支持獲取上傳文件的進度信息,因此我們可以根據進度信息可以實時的顯示文件的上傳進度。
1. 需要注冊 xhr.upload.onprogress = function(e){} 事件,用於監聽文件上傳的進度.注意:需要在send之前注冊。
2. 上傳的進度信息會存儲事件對象e中
3. e.loaded表示已上傳的大小 e.total表示整個文件的大小
代碼參考:
xhr.upload.onprogress = function (e) {
inner.style.width = (e.loaded/e.total*100).toFixed(2)+"%";
span.innerHTML = (e.loaded/e.total*100).toFixed(2)+"%";
}
xhr.send(formData);
如果上傳文件超過8M,php會報錯,需要進行設置,允許php上傳大文件。
跨域資源共享(CORS) ( 兼容性IE10+ )
cors的使用
新版本的XMLHttpRequest對象,可以向不同域名的服務器發出HTTP請求。這叫做"跨域資源共享"(Cross-origin resource sharing,簡稱CORS)。
跨域資源共享(CORS)的前提
-
瀏覽器支持這個功能( 兼容性IE10+ )
-
服務器必須允許這種跨域。
服務器允許跨域的代碼:
//允許所有的域名訪問這個接口
header("Access-Control-Allow-Origin:*");
//允許www.study.com這個域名訪問這個接口
header("Access-Control-Allow-Origin:http://www.study.com");
CORS的具體流程(了解)
-
瀏覽器發送跨域請求
-
服務器端收到一個跨域請求后,在響應頭中添加Access-Control-Allow-Origin Header資源權限配置。發送響應
-
瀏覽器收到響應后,查看是否設置了
header('Access-Control-Allow-Origin:請求源域名或者*');
如果當前域已經得到授權,則將結果返回給JavaScript。否則瀏覽器忽略此次響應。
結論:
-
跨域行為是瀏覽器行為,響應是回來了的, 只是瀏覽器安全機制做了限制, 對於跨域響應內容進行了忽略。
-
服務器與服務器之間是不存在跨域的問題的
jsonp與cors的對比
-
jsonp兼容性好,老版本瀏覽器也支持,但是jsonp僅支持get請求,發送的數據量有限。使用麻煩
-
cors需要瀏覽器支持cors功能才行。但是使用簡單,只要服務端設置允許跨域,對於客戶端來說,跟普通的get、post請求並沒有什么區別。
-
跨域的安全性問題:因為跨域是需要服務端配合控制的 ,也就是說不論jsonp還是cors,如果沒有服務端的允許,瀏覽器是沒法做到跨域的。