跨域
1. 相關(guān)概念
同源策略是瀏覽器的一種安全策略,所謂同源是指 域名,協(xié)議,端口 完全相同,只有同源的地址才可以相互通過
AJAX 的方式請求。
同源或者不同源說的是兩個地址之間的關(guān)系,不同源地址之間請求我們稱之為跨域請求什么是同源?例如:http://www./detail.html 與一下地址對比
對比地址 | 是否同源 | 原因 |
---|
http://api./detail.html | 不同源 | 域名不同 |
https://www./detail.html | 不同源 | 協(xié)議不同 |
http://www.:8080/detail.html | 不同源 | 端口不同 |
http://api.:8080/detail.html | 不同源 | 域名、端口不同 |
https://api./detail.html | 不同源 | 協(xié)議、域名不同 |
https://www.:8080/detail.html | 不同源 | 端口、協(xié)議不同 |
http://www./other.html | 同源 | 只是目錄不同 |
2. 解決方案
現(xiàn)代化的 Web 應(yīng)用中肯定會有不同源的現(xiàn)象,所以必然要解決這個問題,從而實現(xiàn)跨域請求。
2.1. JSONP
JSON with Padding,是一種借助于 script 標簽發(fā)送跨域請求的技巧。
其原理就是在客戶端借助 script 標簽請求服務(wù)端的一個動態(tài)網(wǎng)頁(php 文件),服務(wù)端的這個動態(tài)網(wǎng)頁返回一段帶有函數(shù)調(diào)用的 JavaScript 全局函數(shù)調(diào)用的腳本,將原本需要返回給客戶端的數(shù)據(jù)傳遞進去。
以后絕大多數(shù)情況都是采用 JSONP 的手段完成不同源地址之間的跨域請求客戶端 http://www./users-list.html
<script src="http://api./users.php?callback=foo"></script>
服務(wù)端 http://api./users.php?callback=foo 返回的結(jié)果
foo(['我', '是', '你', '原', '本', '需', '要', '的', '數(shù)', '據(jù)'])
總結(jié)一下:由于 XMLHttpRequest 無法發(fā)送不同源地址之間的跨域請求,所以我們必須要另尋他法,script 這種方案就是我們最終選擇的方式,我們把這種方式稱之為 JSONP,如果你不了解原理,先記住怎么用,多用一段時間再來看原理。
問題:
- JSONP 需要服務(wù)端配合,服務(wù)端按照客戶端的要求返回一段 JavaScript 調(diào)用客戶端的函數(shù)
- 只能發(fā)送 GET 請求
注意:JSONP 用的是 script 標簽,更 AJAX 提供的 XMLHttpRequest 沒有任何關(guān)系!!!
jQuery 中使用 JSONP 就是將 dataType 設(shè)置為 jsonp其他常見的 AJAX 封裝 庫:
2.2. CORS
Cross Origin Resource Share,跨域資源共享
// 允許遠端訪問
header('Access‐Control‐Allow‐Origin: *');
這種方案無需客戶端作出任何變化(客戶端不用改代碼),只是在被請求的服務(wù)端響應(yīng)的時候添加一個 Access- Control-Allow-Origin 的響應(yīng)頭,表示這個資源是否允許指定域請求。
cors代碼
cors.php
<?php
$conn = mysqli_connect('localhost', 'root', '123456', 'demo');
$query = mysqli_query($conn, 'select * from users');
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
// 一行代碼搞定
// 允許跨域請求
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
echo json_encode($data);
cors.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AJAX 跨域(CORS)</title>
<!-- Cross Origin Resource Share -->
</head>
<body>
<script src="jquery.js"></script>
<script>
$.get('http://localhost/cors.php', {}, function (res) {
console.log(res)
})
</script>
</body>
</html>
3. jsonp案例
server.php
<?php
$conn = mysqli_connect('localhost', 'root', '123456', 'test');
$query = mysqli_query($conn, 'select * from users');
while ($row = mysqli_fetch_assoc($query)) {
$data[] = $row;
}
if (empty($_GET['callback'])) {
header('Content-Type: application/json');
echo json_encode($data);
exit();
}
// 如果客戶端采用的是 script 標記對我發(fā)送的請求
// 一定要返回一段 JavaScript
header('Content-Type: application/javascript');
$result = json_encode($data);
$callback_name = $_GET['callback'];
echo "typeof {$callback_name} === 'function' && {$callback_name}({$result})";
client.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP 客戶端</title>
</head>
<body>
<script>
// var script = document.createElement('script')
// script.src = 'http://localhost/jsonp/server.php'
// document.body.appendChild(script)
// function aaabbb (data) {
// console.log('1111', data)
// }
// var script = document.createElement('script')
// script.src = 'http://localhost/jsonp/server.php'
// document.body.appendChild(script)
// function aaabbb (data) {
// console.log('2222', data)
// }
// 為每一個請求創(chuàng)建一個唯一的函數(shù)
// var funcName = 'haha_' + Date.now() + Math.random().toString().substr(2, 5)
// var script = document.createElement('script')
// script.src = 'http://localhost/jsonp/server.php?callback=' + funcName
// document.body.appendChild(script)
// window[funcName] = function (data) {
// console.log('1111', data)
// }
function jsonp (url, params, callback) {
var funcName = 'jsonp_' + Date.now() + Math.random().toString().substr(2, 5)
if (typeof params === 'object') {
var tempArr = []
for (var key in params) {
var value = params[key]
tempArr.push(key + '=' + value)
}
params = tempArr.join('&')
}
var script = document.createElement('script')
script.src = url + '?' + params + '&callback=' + funcName
document.body.appendChild(script)
window[funcName] = function (data) {
callback(data)
delete window[funcName]
document.body.removeChild(script)
}
}
jsonp('http:///jsonp/server.php', { id: 123 }, function (res) {
console.log(res)
})
</script>
</body>
</html>