【前端Tip】同源策略以及實(shí)現(xiàn)跨域請求

1. 同源策略和跨域

1.1 什么是同源

如果兩個(gè)頁面的協(xié)議选浑、域名端口都相同惩歉,則說明兩個(gè)頁面具有相同的源激率。比如下面的圖片娄帖,給出了相對于http://www.test.com/index.html頁面的同源監(jiān)測:

image.png

1.2 什么是同源策略

同源策略(Same origin policy)是一種約定姻成,它是瀏覽器最核心也是最基本的安全功能插龄,如果缺少了同源策略,則瀏覽器的正常功能可能都會(huì)收到影響科展【危可以說Web是構(gòu)建在同源策略基礎(chǔ)之上的,瀏覽器只是針對同源策略的一種實(shí)現(xiàn)才睹。

它的核心就在于它認(rèn)為從任何站點(diǎn)裝載的信息內(nèi)容都是不安全的徘跪,它們應(yīng)該只被允許訪問來自同一站點(diǎn)的資源,而不是那些來自其它站點(diǎn)可能懷有惡意的資源琅攘。

另外垮庐,同源策略分為以下兩種:

  • DOM同源策略
    禁止對不同源頁面DOM進(jìn)行操作。這里主要場景是iframe跨域的情況乎澄,不同域名的iframe是限制互相訪問的突硝;

  • XMLHttpRequest同源策略
    禁止使用XHR對象向不同源的服務(wù)器地址發(fā)起HTTP請求;

1.3 為什么要有跨域限制

因?yàn)榇嬖跒g覽器同源策略置济,所以才會(huì)有跨域問題解恰。那么瀏覽器是出于何種原因會(huì)有跨域的限制呢锋八?其實(shí)很簡單,就是為了用戶的上網(wǎng)安全护盈。

1.3.1 如果沒有DOM同源策略

如果沒有DOM同源策略挟纱,也就是說不同域的iframe之間可以相互訪問,那么黑客可以這樣進(jìn)行攻擊:

  1. 做一個(gè)假網(wǎng)站腐宋,里面用iframe嵌套一個(gè)銀行網(wǎng)站:http://testbank.com
  2. 把iframe寬高調(diào)整到頁面全部紊服,這樣用戶進(jìn)來之后除了域名,別的部分和銀行的網(wǎng)站沒有任何區(qū)別胸竞;
  3. 如果用戶輸入賬號(hào)密碼欺嗤,我們的主網(wǎng)站可以跨域訪問到http://testbank.com的dom節(jié)點(diǎn),就可以拿到用戶的賬戶密碼了卫枝;

1.3.2 如果沒有XMLHttpRequest同源策略

如果沒有XMLHttpRequest同源策略煎饼,那么黑客可以進(jìn)行CSRF(跨站請求偽造)攻擊:

  1. 用戶登錄了自己的銀行頁面http://testbank.com,向用戶的cookie中添加用戶標(biāo)識(shí)校赤;
  2. 用戶瀏覽了惡意頁面 http://evil.com吆玖,執(zhí)行了頁面中的AJAX請求代碼;
  3. 請求中會(huì)默認(rèn)攜帶cookie马篮;
  4. 銀行頁面從發(fā)送的cookie中提取用戶標(biāo)識(shí)沾乘,驗(yàn)證用戶無誤,response中返回請求數(shù)據(jù)浑测,此時(shí)數(shù)據(jù)就泄露了翅阵。由于Ajax在后臺(tái)執(zhí)行,用戶無法感知這一過程尽爆;

因此怎顾,有了瀏覽器的同源策略读慎,才能讓我們更安全的上網(wǎng)漱贱。

1.3.3 跨域

從上面我們了解到了瀏覽器同源策略的作用,也正是有了跨域限制夭委,才使我們能安全的上網(wǎng)幅狮。但是在實(shí)際開發(fā)中,我們需要突破這樣的限制株灸。

出現(xiàn)跨域的根本原因是:瀏覽器的同源策略不允許非同源的URL之間進(jìn)行資源的交互崇摄。

比如:
當(dāng)前的網(wǎng)頁是:http://www.test.com/index.html
請求的接口是:http:www.api.com/userlist

下圖中展示的是瀏覽器對跨域請求的攔截:


image.png

從上圖中我們可以看到,瀏覽器是允許發(fā)起請求慌烧,但是逐抑,跨域請求回來的數(shù)據(jù),會(huì)被瀏覽器攔截屹蚊,無法被頁面獲取到厕氨。

2. 如何實(shí)現(xiàn)跨域請求

實(shí)現(xiàn)跨域數(shù)據(jù)請求方法有很多进每,比如JSONPCORS命斧、使用Proxy等田晚。

  • JSONP:
    出現(xiàn)的早,兼容性好(兼容低版本IE)国葬。是前端程序員為了解決跨域問題贤徒,被迫想出來的一種臨時(shí)解決方案。缺點(diǎn)是只支持 GET 請求汇四,不支持 POST 請求接奈;

  • CORS:
    出現(xiàn)的較晚,它是 W3C 標(biāo)準(zhǔn)通孽,屬于跨域 AJAX 請求的根本解決方案鲫趁。支持 GET 和 POST 請求。缺點(diǎn)是不兼容某些低版本的瀏覽器利虫。

  • Proxy:
    既然跨域是瀏覽器導(dǎo)致的挨厚,那我們可以使用代理繞開瀏覽器,這也是常見的解決跨域的方案糠惫;

3. JSONP

JSONP(JSON with Padding)是JSON的一種使用模式疫剃,可用于解決主流瀏覽器跨域數(shù)據(jù)訪問的問題。

3.1 JSONP原理

由于script標(biāo)簽不受瀏覽器同源策略的影響硼讽,允許跨域引用資源巢价。因此,可以通過動(dòng)態(tài)創(chuàng)建script標(biāo)簽固阁,然后利用src屬性進(jìn)行跨域壤躲。

  1. 事先定義一個(gè)用于獲取跨域響應(yīng)數(shù)據(jù)的回調(diào)函數(shù);
  2. 創(chuàng)建一個(gè)script標(biāo)簽發(fā)起一個(gè)請求(將回調(diào)函數(shù)的名稱作為參數(shù)放到這個(gè)請求的query參數(shù)里)备燃;
  3. 服務(wù)端獲取到這個(gè)請求之后碉克,獲取query中的回調(diào)函數(shù)的名稱,并將數(shù)據(jù)放到回調(diào)函數(shù)的參數(shù)里并齐,作為請求的響應(yīng)漏麦;
  4. 前端的script標(biāo)簽收到請求的響應(yīng)之后,會(huì)立馬執(zhí)行這個(gè)回調(diào)函數(shù)况褪,于是撕贞,就可以在之前定義的回調(diào)函數(shù)中獲取到數(shù)據(jù)了;

3.2 示例

3.2.1 前端請求示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // 1.定義一個(gè)回調(diào)函數(shù)测垛,用來接收返回的數(shù)據(jù)
        function getData(data) {
            console.log('data :>> ', data);
        }
    </script>
        <!-- 2.使用script標(biāo)簽(也可以動(dòng)態(tài)創(chuàng)建)捏膨,并且告訴后端回調(diào)函數(shù)的名字是getData -->
        <!-- 3.通過script.src 請求 http://localhost:8080/api/data?cb=getData-->
        <!-- 4.后端識(shí)別這樣的url格式并處理該請求,然后返回 getData('hello') 給瀏覽器 -->
        <!-- 5.瀏覽器在接收到 getData('hello') 數(shù)據(jù)之后食侮,會(huì)執(zhí)行 getData 方法号涯,獲得后端返回的數(shù)據(jù) -->
    <script src="http://localhost:8080/api/data?cb=getData"></script>
</body>
</html>

3.2.2 后端響應(yīng)示例

const http = require('http')
const url = require('url')

const server = http.createServer((req, res)=>{
    let urlString = req.url
    let urlObj = url.parse(urlString, true)
    res.write(`${urlObj.query.cb}("hello")`)
    res.end()
})
server.listen(8080, () => {
    console.log("localhost:8080");
})

3.3 優(yōu)/缺點(diǎn)

優(yōu)點(diǎn):

  • 使用簡單熬北,沒有兼容性問題;
  • 請求完畢之后可以通過調(diào)用callback的方式回傳結(jié)果诚隙;

缺點(diǎn):

  • 只支持GET請求讶隐,不支持POST等其它類型的請求;
  • 由于是從其它域中加載代碼執(zhí)行久又,因此如果其他域不安全巫延,很可能會(huì)在響應(yīng)中夾帶一些惡意代碼;
  • 只支持HTTP請求這種情況地消,不能解決不同域的兩個(gè)頁面之間如何進(jìn)行JavaScript調(diào)用的問題炉峰;
  • 要確定JSONP請求是否失敗并不容易,雖然H5給script標(biāo)簽新增了一個(gè)onerror事件處理程序脉执,但是存在兼容性問題疼阔;

4.CORS

之前在學(xué)習(xí)OPTIONS預(yù)檢請求的時(shí)候,已經(jīng)總結(jié)過了半夷。詳見:http://www.reibang.com/p/d9d30dc9898b

5. Proxy

簡單來說婆廊,就是請求自己同源的服務(wù)(代理),然后通過代理去請求跨域的資源巫橄。常用的解決方案一般是兩種:本地代理和nginx反向代理淘邻。

5.1 本地代理

開發(fā)環(huán)境,前端處理湘换。

無論是 webpack 還是 vite 都內(nèi)置了本地代理宾舅。這讓我們能夠在不依賴后端的前提下解決跨域的問題(僅僅是本地開發(fā)環(huán)境下, 線上環(huán)境需要 nginx 配置反向代理)

webpack的處理方式如下:

module.exports = {
  //...
  devServer: {
    proxy: {
      '/api': 'http://localhost:3000'
    }
  }
};

vite的處理方式:

export default defineConfig({
  // ...
  server: {
    proxy: {
      '/api': {
        target: 'http://jsonplaceholder.typicode.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
});

5.2 nginx反向代理

生產(chǎn)環(huán)境一般用 nginx 托管部署我們的前端代碼包。處理跨域問題需要 nginx 配置反向代理彩倚。

server {
    listen: 8001;
    server_name 10.2.2.25;

    location ~ /api/ {
        proxy_pass http://127.0.0.1:8081;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筹我,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子帆离,更是在濱河造成了極大的恐慌蔬蕊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盯质,死亡現(xiàn)場離奇詭異袁串,居然都是意外死亡概而,警方通過查閱死者的電腦和手機(jī)呼巷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赎瑰,“玉大人王悍,你說我怎么就攤上這事〔吐” “怎么了压储?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵鲜漩,是天一觀的道長。 經(jīng)常有香客問我集惋,道長孕似,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任刮刑,我火速辦了婚禮喉祭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雷绢。我一直安慰自己泛烙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布翘紊。 她就那樣靜靜地躺著蔽氨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪帆疟。 梳的紋絲不亂的頭發(fā)上鹉究,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音踪宠,去河邊找鬼坊饶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛殴蓬,可吹牛的內(nèi)容都是我干的匿级。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼染厅,長吁一口氣:“原來是場噩夢啊……” “哼痘绎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肖粮,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬榮一對情侶失蹤孤页,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后涩馆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體行施,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年魂那,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛾号。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡涯雅,死狀恐怖鲜结,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤精刷,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布拗胜,位于F島的核電站,受9級(jí)特大地震影響怒允,放射性物質(zhì)發(fā)生泄漏埂软。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一纫事、第九天 我趴在偏房一處隱蔽的房頂上張望仰美。 院中可真熱鬧,春花似錦儿礼、人聲如沸咖杂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诉字。三九已至,卻和暖如春知纷,著一層夾襖步出監(jiān)牢的瞬間壤圃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來泰國打工琅轧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伍绳,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓乍桂,卻偏偏與公主長得像冲杀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子睹酌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353