同源與跨域
1. 同源
同源策略的基本概念
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 請求不能發送。
雖然這些限制是很有必要的,但是也給我們日常開發帶來不好的影響。比如實際開發過程中,往往都會把服務器端架設到一臺甚至是一個集羣的服務器中,把客戶端頁面放到另外一個單獨的服務器。那麼這時候就會出現不同源的情況,如果我們知道兩個網站都是安全的話,我們是希望兩個不同源的網站之間可以相互請求數據的。這就需要使用到跨域 。
2. 跨域
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)";
// jsonp: json width padding
//echo "$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 請求資源時, 不受同源策略的限制.
-
在服務端返回一個函數的調用,將數據當前調用函數的實參。
-
在瀏覽器通過形端,需要程序要聲明一個全局函數,參就可以獲取到服務端返回的對應的值
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);
}
});
【案例:查詢天氣.html】 [天氣查詢api地址](https://www.jisuapi.com/api/weather/)
【案例:省市區三級聯動.html】 api地址 (https://www.jisuapi.com/api/area/)
//天氣.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
table {
margin-top: 20px;
width: 500px;
border: 1px solid #000;
border-collapse: collapse;
}
td {
height: 30px;
line-height: 30px;
border: 1px solid #000;
}
</style>
</head>
<body>
<input type="text" id="city">
<input type="button" value="查詢" id="btn">
<table id="showTable">
</table>
<script src="template-web.js"></script>
<script src="jquery-1.12.4.js"></script>
<script type="text/html" id="tpl">
<tr>
<td>城市</td>
<td>{{ result.city }}</td>
</tr>
<tr>
<td>日期</td>
<td>{{ result.date }}</td>
</tr>
<tr>
<td>天氣</td>
<td>{{ result.weather }}</td>
</tr>
<tr>
<td>氣溫</td>
<td>{{ result.temp }}度</td>
</tr>
<tr>
<td>最高氣溫</td>
<td>{{ result.temphigh }}度</td>
</tr>
<tr>
<td>最低氣溫</td>
<td>{{ result.templow }}度</td>
</tr>
<tr>
<td>更新時間</td>
<td>{{ result.updatetime }}</td>
</tr>
</script>
<script>
// 思路: 1. 點擊按鈕, 獲取文本框的內容, 發送 jsonp 請求(跨域了), 請求數據,
// 2. 得到數據, 通過模板引擎來渲染
$(function() {
$('#btn').click(function() {
$.ajax({
type: "get",
url: "http://api.jisuapi.com/weather/query",
data: {
appkey: "3fa977031a30ffe1",
city: $('#city').val()
},
dataType: "jsonp",
success: function( info ) {
console.log( info )
// 模板 id, 數據對象
var htmlStr = template( "tpl", info );
$('#showTable').html( htmlStr );
}
});
});
})
</script>
</body>
</html>
//省市區.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
div {
margin: 50px;
}
</style>
</head>
<body>
<div>
<select id="s1">
<option>請選擇</option>
</select>
<select id="s2">
<option>請選擇</option>
</select>
<select id="s3">
<option>請選擇</option>
</select>
</div>
<script src="jquery-1.12.4.js"></script>
<script src="template-web.js"></script>
<script type="text/html" id="tpl">
<option>請選擇</option>
{{ each result v i }}
<option value="{{ v.id }}">{{ v.name }}</option>
{{ /each }}
</script>
<script>
// 兩個核心點
// 1. 監聽 select 變化, 用change
// 2. 獲取選中的內容 id, 通過 select 的 value 來獲取
// 思路:
// 1. 一進入頁面, 請求省的數據
// 2. 用戶選擇了省, 請求市的數據
// 3. 用戶選擇了市, 請求區的數據
$.ajax({
type: "get",
url: "http://api.jisuapi.com/area/province",
data: {
appkey: "3fa977031a30ffe1" // 用戶唯一標識, 每個人的個人中心可以查看
},
dataType: "jsonp",
success: function( info ) {
console.log( info );
var htmlStr = template( "tpl", info );
$('#s1').html( htmlStr );
}
});
// change 事件, 當select被選擇的內容改變時觸發
$('#s1').change(function() {
// 重置市和區的數據
$('#s2').html("<option>請選擇</option>");
$('#s3').html("<option>請選擇</option>");
// console.log( "選擇的內容改變了" );
// 發送 ajax 請求, 請求市的數據
$.ajax({
type: "get",
url: "http://api.jisuapi.com/area/city",
data: {
parentid: $('#s1').val(), // 獲取選中的省的 id
appkey: "3fa977031a30ffe1"
},
dataType: "jsonp",
success: function( info ) {
var htmlStr = template( "tpl", info );
$('#s2').html( htmlStr );
}
})
});
$('#s2').change(function() {
// 發送ajax請求, 獲取區的數據
$.ajax({
type: "get",
url: "http://api.jisuapi.com/area/town",
data: {
parentid: $('#s2').val(), // 獲取選中的城市 id
appkey: "3fa977031a30ffe1"
},
dataType: "jsonp",
success: function( info ) {
var htmlStr = template( "tpl", info );
$('#s3').html( htmlStr );
}
})
})
</script>
</body>
</html>