JSONP CORS 與代理解決跨域
一.背景
在我們日常的工作開發中,經常遇到跨域問題,特別是前後端分離的開發模式下,跨域問題也是非常常見的.
二.爲什麼會出現跨域?
在JavaScript中,有一個很重要的安全性限制,被稱爲“Same-Origin Policy”(同源策略)。這一策略對於JavaScript代碼能夠訪問的頁面內容做了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內容。
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說 Web 是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。
它的核心就在於它認爲自任何站點裝載的信賴內容是不安全的。當被瀏覽器半信半疑的腳本運行在沙箱時,它們應該只被允許訪問來自同一站點的資源,而不是那些來自其它站點可能懷有惡意的資源。
瀏覽器的同源策略,是對JavaScript實施的安全限制。
所謂的同源是指,域名、協議、端口均爲相同。
當前頁面url | 被請求頁面url | 是否跨域 | 原因 |
---|---|---|---|
http://www.hello.com/ | http://www.hello.com/a.html | 否 | 同源(協議,域名,端口相同) |
http://www.hello.com/ | https://www.hello.com/a.html | 跨域 | 協議不同(http/https) |
http://www.hello.com/ | http://sz.hello.com/ | 跨域 | 子域名不同(www/sz) |
http://www.hello.com:8080 | http://www.hello.com:7070 | 跨域 | 端口號不同(8080/7070) |
http://localhost:8080 | http://127.0.0.1:8080 | 跨域 | 子域名不同(localhost/127.0.0.1) |
三.解決跨域的方式
幾種主流的跨域解決方案
JSONP處理跨域
1. 原理:
jsonp是一種跨域通信的手段,它的原理其實很簡單:
a:首先是利用script標籤的src屬性來實現跨域。
b:通過將前端方法作爲參數傳遞到服務器端,然後由服務器端注入參數之後再返回,實現服務器端向客 戶端通信。
c: 由於使用script標籤的src屬性,因此只支持get方法
2.實現方式:
//創建script標籤
function createScript(){
//定義一個script標籤
let oScript = document.createElement("script");
//script的src屬性 直接賦值爲需要跨域的接口鏈接
//並且定義callback參數,後面跟一個回調函數
oScript.src = `https://api.asilu.com/today?callback=getTodays`;
//將生成的script追加到我們的dom中
document.body.appendChild(oScript);
}
//callback對於的函數
function getTodays(data) {
//獲取跨域得到的對象數據
let oTodays = data.data;
}
上述就是通過jsonp 實現了一個跨域請求,獲取天氣的數據.既然叫jsonp,顯然目的還是json,而且是跨域獲取,利用js創建一個script標籤,把json的url賦給script的scr屬性,把這個script插入到頁面裏,讓瀏覽器去跨域獲取資源,callback是頁面存在的回調方法,參數就是想得到的json,回調方法要遵從服務端的約定一般是用 callback 或者 cb,特別注意的是jsonp只針對get形式的請求
3.優缺點
優點:使用簡便,沒有兼容性問題。
缺點:
只支持 GET 請求。
前後端需要配合。
由於是從其它域中加載代碼執行,因此如果其他域不安全,很可能會在響應中夾帶一些惡意代碼。
CORS跨域資源共享(xhr2)
1.原理
CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)- 它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制- 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與- 對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣- 實現CORS通信的關鍵是服務端,只要服務端實現了CORS接口,就可以跨源通信
2.實現
實現CORS並不難,只需服務端做一些設置即可:如
// 控制允許的請求來源
header("Access-Control-Allow-Origin:*"); // *表示允許任何來源 基本上設置這個就可以了
//另外,還有一些需要設置put請求 delete請求的,還有自定義請求頭,nodejs默認是get,post
// 控制允許的自定義請求頭
'Access-Control-Allow-Headers': 'abc,efg',
// 控制允許的請求方式
'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE'
注意:IE10以下不支持CORS
3.優缺點
優點:
CORS 通信與同源的 AJAX 通信沒有差別,代碼完全一樣,容易維護。
支持所有類型的 HTTP 請求。
缺點
存在兼容性問題,特別是 IE10 以下的瀏覽器。
第一次發送非簡單請求時會多一次請求。
服務器代理跨域
1.原理:
通過服務端語言代理請求。如PHP,C# 服務端語言是沒有跨域限制的。- 讓服務器去別的網站獲取內容然後返回頁面。因爲同源策略是針對瀏覽器的安全性限制.服務器不存在跨域問題,所以可以由服務器請求所要域的資源再返回給客戶端。
我們在請求第三方的數據時,通過瀏覽器的ajax請求肯定是不行的,因爲人家也不會給你設置cors,所以我們這邊的服務器代理解決跨域的方式就應運而生了.
2.實現
我這邊通過nodejs+express實現服務代理跨域
需要安裝中間件 http-proxy-middleware
$ npm install http-proxy-middleware
服務端代碼
// 通過代理解決跨域問題
/**
* 使用nodejs啓動一個代理服務
* 第三方的 http-proxy-middleware 中間件
*/
//引入express
const express = require('express');
//引入代理中間件
const { createProxyMiddleware } = require('http-proxy-middleware');
//聲明express實例
const app = express();
// 註冊一個cors的中間件,允許跨域
app.use((req, res, next) => {
res.set({
//允許所有域請求,實際開發中,*需要改成對應的域名
'Access-Control-Allow-Origin': '*',
// 設置允許前端傳遞的那些自定義請求頭
'Access-Control-Allow-Headers': 'token',
// 設置允許的請求方式
'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE'
});
//必須要寫,將控制權交給下一個
next();
});
// 註冊一個代理中間件
// 前端服務:http://localhost:8080
// 代理服務:http://localhost:3000
// 目標服務:http://m.maoyan.com
app.use('/api', createProxyMiddleware({
//配置選項
// 目標地址:只需要協議和主機
target: "http://m.maoyan.com",
/**
* 路徑重寫
*
* 當我們通過代理訪問目標接口的流程大致如下
* http://localhost:8080 -> 請求 ->
* http://localhost:3000/api/ajax/movieOnInfoList -> 代理到 ->
* http://m.maoyan.com/api/ajax/movieOnInfoList
* 這時真實的目標接口地址是沒有 /api 這個前綴的。所以會導致請求失敗 404 等問題
*
* 設置如下的路徑重寫規則之後,流程如下
* http://localhost:8080 -> 請求 ->
* http://localhost:3000/api/ajax/movieOnInfoList -> 代理到 ->
* http://m.maoyan.com/ajax/movieOnInfoList
*/
pathRewrite: {
"^/api": ""
},
// 虛擬託管站點所需, 一般直接設置爲true就好
changeOrigin: true
}));
//監聽3000端口,啓動服務
app.listen(3000, () => {
console.log("代理服務啓動成功");
});
前端代碼
<script>
$(function () {
$.ajax({
url: 'http://localhost:3000/api/ajax/movieOnInfoList',
type: 'GET',
success: function (res) {
console.log(res)
}
})
})
</script>
上面是3中主流的跨域解決方案,還有其他的解決方式,有興趣可以大家去研究:JSONP、CORS、服務器代理、document.domain、window.name、location.hash、postMessage ,nginx代理跨域