一.什麼是同源政策?
同源策略是指在Web瀏覽器中,允許某個網頁尾本訪問另一個網頁的資料,但前提是這兩個網頁必須有相同的URI、主機名和埠號,一旦兩個網站滿足上述條件,這兩個網站就被認定為具有相同來源。此策略可防止某個網頁上的惡意指令碼通過該頁面的文件物件模型訪問另一網頁上的敏感資料。同源策略對Web應用程式具有特殊意義,因為Web應用程式廣泛依賴於HTTP cookie來維持使用者會話,所以必須將不相關網站嚴格分隔,以防止丟失資料洩露。值得注意的是同源策略僅適用於指令碼,這意味著某網站可以通過相應的HTML標籤訪問不同來源網站上的影像、CSS和動態載入指令碼等資源。而跨站請求偽造就是利用同源策略不適用於HTML標籤的缺陷——維基百科
舉個例子:
總結就是:同一協議,同一域名,同一埠號
正如上面所說,因為瀏覽器同源政策的限制,非同源下的請求,都會產生跨域的問題,jsonp則是解決這一問題的簡便方法之一。
二.如何突破同源策略限制?
在上面關於同源策略的描述中有一句話:同源策略僅適用於指令碼
這意味著我們可以將訪問的連結放在HTML中,這樣就可以繞過同源策略的干擾,實現跨域。
- 比如我們可以將跨域的請求放在script標籤中
<!-- 將非同源伺服器端的請求地址寫在script標籤的src屬性中 -->
<script src="http://localhost:3001/better?callback=fn2"></script>
- 因此我們可以動態建立script標籤來使用json方法獲取資料
var btn = document.getElementById('btn');
btn.onclick = function () {
// 建立script標籤
var script = document.createElement('script');
// 設定src屬性
script.src = 'http://localhost:3001/better?callback=fn2';
// 將srcipt標籤追加到頁面中
document.body.appendChild(script);
// 為script標籤新增onload事件
script.onload = function () {
// 將body中的script標籤刪除掉
document.body.removeChild(script);
}
}
- 基於以上可以封裝一個簡單的jsonp方法
function jsonp(options) {
// 動態建立script標籤
var script = document.createElement('script')
// 拼接傳入的資料
var params = ''
for (var key in options.data) {
params += '&' + key + '=' + options.data[key]
}
// 建立隨機函式名,防止路徑相同
var fnName = 'myJsonp' + Math.random().toString().replace('.', '')
// 讓函式中的success變成全域性函式
window[fnName] = options.success
// 為script標籤新增src屬性
script.src = options.url + '?callback=' + fnName + params
// 將script標籤追加到頁面中
document.body.appendChild(script)
// 為script標籤新增onload事件
script.onload = function () {
// 刪除script標籤
document.body.removeChild(script)
}
}
- 使用方法如下
jsonp({
// 請求地址
url: 'http://localhost:3001/better',
// 請求成功
success: function (data) {
console.log('函式呼叫成功')
console.log(data)
},
})
三.使用jsonp第三方庫完成封裝
以下是NPM中jsonp第三方庫的關鍵函式jsonp介紹:
- 下載第三方jsonp庫
npm install jsonp -S
- 封裝jsonp方法
// 匯入jsonp模組
import originJSONP from 'jsonp'
// 封裝json函式並匯出
export default function jsonp(url, data, option) {
url += (url.indexOf('?') < 0 ? '?' : '&') + param(data)
return new Promise((resolve, reject) => {
originJSONP(url, option, (err, data) => {
if (!err) {
resolve(data)
} else {
reject(err)
}
})
})
}
// 引數處理
function param(data) {
let url = ''
for (var k in data) {
const value = data[k] !== undefined ? data[k] : ''
url += `&${k}=${encodeURIComponent(value)}`
}
return url ? url.substring(1) : ''
}