跨域請(qǐng)求Jsonp和CORS

同源策略

同源策略(Same origin policy)是一種約定术健,它是瀏覽器最核心也最基本的安全功能毡琉,如果缺少了同源策略蕾久,則瀏覽器的正常功能可能都會(huì)受到影響婉宰「璨颍可以說(shuō)Web是構(gòu)建在同源策略基礎(chǔ)之上的,瀏覽器只是針對(duì)同源策略的一種實(shí)現(xiàn)心包。

同源策略类咧,它是由Netscape提出的一個(gè)著名的安全策略。現(xiàn)在所有支持JavaScript 的瀏覽器都會(huì)使用這個(gè)策略蟹腾。所謂同源是指痕惋,域名,協(xié)議娃殖,端口相同值戳。當(dāng)一個(gè)瀏覽器的兩個(gè)tab頁(yè)中分別打開(kāi)來(lái) 百度和谷歌的頁(yè)面當(dāng)瀏覽器的百度tab頁(yè)執(zhí)行一個(gè)腳本的時(shí)候會(huì)檢查這個(gè)腳本是屬于哪個(gè)頁(yè)面的,即檢查是否同源炉爆,只有和百度同源的腳本才會(huì)被執(zhí)行堕虹。如果非同源,那么在請(qǐng)求數(shù)據(jù)時(shí)芬首,瀏覽器會(huì)在控制臺(tái)中報(bào)一個(gè)異常赴捞,提示拒絕訪問(wèn)。

同源限制

同源策略限制以下幾種行為:

  • Cookie郁稍、LocalStorage 和 IndexDB 無(wú)法讀取
  • DOM 和 Js對(duì)象無(wú)法獲得
  • AJAX 請(qǐng)求不能發(fā)送

常見(jiàn)跨域場(chǎng)景

URL                                      說(shuō)明                    是否允許通信
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名螟炫,不同文件或路徑           允許
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不同端口                不允許
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名艺晴,不同協(xié)議                不允許
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名對(duì)應(yīng)相同ip              不允許
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不同                不允許
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不同域名                         不允許

情景示例

因?yàn)檎迷趯W(xué)習(xí)Django掸屡,所以下面例子都用Django做為示例

站點(diǎn)2向站點(diǎn)1請(qǐng)求數(shù)據(jù)

站點(diǎn)1(稱為demo1)

#view.py

from django.shortcuts import render,HttpResponse

# Create your views here.


def showjson(request):
    import json
    data = {"status":True,"msg":"test"}
    return HttpResponse(json.dumps(data))

站點(diǎn)2(demo2)


#views.py
from django.shortcuts import render,HttpResponse

# Create your views here.

def getjson(request):
    return render(request,'index.html')

--------------------------------------------------------------------
# index.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<button type="button">請(qǐng)求demo1站點(diǎn)</button>
<script>
    $("button").click(function () {
        $.ajax({
            url:'http://127.0.0.1:8000/showjson/',
            type:'GET',
        })
    })
</script>
</body>
</html>

點(diǎn)擊button觸發(fā)ajax封寞,讓demo2請(qǐng)求demo1的showjson,產(chǎn)生錯(cuò)誤

谷歌

谷歌

火狐

火狐

火狐的錯(cuò)誤提示說(shuō)得更加清楚

注意:這里要清楚仅财,其實(shí)請(qǐng)求已經(jīng)發(fā)出去了狈究,只是在接收返回到瀏覽器時(shí),由于同源策略盏求,被攔截讀取

src屬性

那同樣是請(qǐng)求為何我們用jQuery的ajax去get請(qǐng)求會(huì)產(chǎn)生錯(cuò)誤抖锥,而script的src引入?yún)s沒(méi)有報(bào)跨域請(qǐng)求錯(cuò)誤?

類似的標(biāo)簽還有 img碎罚,link磅废,iframe和script。初步得出個(gè)結(jié)論荆烈,有src屬性的標(biāo)簽允許跨域

Jsonp

什么是jsonp拯勉?維基百科的定義是:JSONP(JSON with Padding)是資料格式 JSON 的一種“使用模式”竟趾。

JSONP也叫填充式JSON,是應(yīng)用JSON的一種新方法宫峦,只不過(guò)是被包含在函數(shù)調(diào)用中的JSON岔帽。

原理是通過(guò)script標(biāo)簽的跨域特性來(lái)繞過(guò)同源策略。(需要服務(wù)器端配合导绷,商量好)

原生實(shí)現(xiàn)

固定函數(shù)名

script標(biāo)簽src其實(shí)就是引入目標(biāo)文件中的內(nèi)容犀勒,然后執(zhí)行該代碼。

  • 我們先定義好func(name)這個(gè)函數(shù)妥曲。
  • script的src引入時(shí)贾费,執(zhí)行該函數(shù)。代碼執(zhí)行成功逾一,命令行打印出test字符串

demo2站點(diǎn) html文件

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    
</head>
<body>


<script>
    function func(name) {
        console.log(name);
    }

</script>
<script src="http://127.0.0.1:8000/showjson/"></script>
</body>
</html>

demo1站點(diǎn)view函數(shù)

def showjson(request):
    
    return HttpResponse("%s(%s)" % ("func","'test'"))

這樣不夠靈活铸本,實(shí)現(xiàn)性價(jià)比太低

自定函數(shù)名

demo2中HTML文件

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    
</head>
<body>


<script>
    function func(name) {
        console.log(name);
    }

</script>
<script src="http://127.0.0.1:8000/showjson/?callback=func"></script>
</body>
</html>

demo1的view

from django.views.decorators.csrf import csrf_exempt
# Create your views here.

@csrf_exempt
def showjson(request):

    callback = request.GET.get("callback")
    return HttpResponse("%s(%s)" % (callback,"'test'"))

通過(guò)添加get參數(shù)callback動(dòng)態(tài)設(shè)置函數(shù)名,前端使用一致函數(shù)名就可以了遵堵。

但是一載入就發(fā)get請(qǐng)求了箱玷,下面改寫為點(diǎn)擊觸發(fā)

模擬創(chuàng)建script

demo2中HTML文件

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>index</title>
    
</head>
<body>
<button type="button" onclick="toget()">請(qǐng)求demo1站點(diǎn)</button>

<script>
    function func(name) {
        console.log(JSON.stringify(name));
    }

    function toget() {
        var script = document.createElement("script");
        script.setAttribute("type","text/javascript");
        script.src = "http://127.0.0.1:8000/showjson/?callback=func";
        document.body.appendChild(script);
        document.body.removeChild(script);
    }
    
</script>

</body>
</html>

demo1中函數(shù)

這次以發(fā)送json數(shù)據(jù)為例

@csrf_exempt
def showjson(request):
    import json
    data = {"status":True,"msg":"test"}
    callback = request.GET.get("callback")
    return HttpResponse("%s(%s)" % (callback,json.dumps(data)))

這樣就能正常獲取json數(shù)據(jù)了

jQuery實(shí)現(xiàn)

<script>
    function toget() {
        $.ajax({
            url:'http://127.0.0.1:8000/showjson/',
            dataType:'jsonp',
            jsonp:'callback',
            jsonpCallback:'func'
        });
    }
    function func(arg) {
        console.log(JSON.stringify(arg))
    }
</script>

這種方式是自己指定回調(diào)執(zhí)行函數(shù),那直接用ajax success回調(diào)函數(shù)更簡(jiǎn)單

<script>
    function toget() {
        $.ajax({
            url:'http://127.0.0.1:8000/showjson/',
            dataType:'jsonp',
            jsonp:'callback',
            success:function (arg) {
                console.log(JSON.stringify(arg))
            }
        });
    }
</script>

這種方式 jQuery自己生產(chǎn)了一個(gè)隨機(jī)的callback參數(shù)值陌宿,去請(qǐng)求然后執(zhí)行锡足。例如上面這個(gè)例子的url

http://127.0.0.1:8000/showjson/?callback=jQuery33105892941255188728_1540691603147&_=1540691603148

CORS

CORS(Cross-Origin Resource Sharing)跨域資源共享,定義了必須在訪問(wèn)跨域資源時(shí)壳坪,瀏覽器與服務(wù)器應(yīng)該如何溝通舶得。CORS背后的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進(jìn)行溝通,從而決定請(qǐng)求或響應(yīng)是應(yīng)該成功還是失敗爽蝴。

普通跨域請(qǐng)求:只服務(wù)端設(shè)置Access-Control-Allow-Origin即可沐批,前端無(wú)須設(shè)置,若要帶cookie請(qǐng)求:前后端都需要設(shè)置

還是以上面最初錯(cuò)誤跨域例子為例蝎亚,只要在demo1被請(qǐng)求(服務(wù)端)返回的response響應(yīng)頭添加字段就行

demo2中HTML

<button type="button" onclick="toget()">請(qǐng)求demo1站點(diǎn)</button>

<script>
    function toget() {
        $.ajax({
            url:'http://127.0.0.1:8000/showjson/',
            success:function (data) {
                console.log(data)
            }
        });
    }
</script>

demo1中view

@csrf_exempt
def showjson(request):
    import json
    data = {"status":True,"msg":"test"}

    res = HttpResponse(json.dumps(data))
    res['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001'
    return res

深入資料參考

前端常見(jiàn)跨域解決方案(全)

跨域資源共享 CORS 詳解

同源策略與Jsonp

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末九孩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子发框,更是在濱河造成了極大的恐慌躺彬,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梅惯,死亡現(xiàn)場(chǎng)離奇詭異宪拥,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)铣减,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門她君,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人葫哗,你說(shuō)我怎么就攤上這事犁河”钫恚” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵桨螺,是天一觀的道長(zhǎng)宾符。 經(jīng)常有香客問(wèn)我,道長(zhǎng)灭翔,這世上最難降的妖魔是什么魏烫? 我笑而不...
    開(kāi)封第一講書人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮肝箱,結(jié)果婚禮上哄褒,老公的妹妹穿的比我還像新娘。我一直安慰自己煌张,他們只是感情好呐赡,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著骏融,像睡著了一般链嘀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上档玻,一...
    開(kāi)封第一講書人閱讀 49,829評(píng)論 1 290
  • 那天怀泊,我揣著相機(jī)與錄音,去河邊找鬼误趴。 笑死霹琼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凉当。 我是一名探鬼主播枣申,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼看杭!你這毒婦竟也來(lái)了糯而?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤泊窘,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后像寒,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體烘豹,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年诺祸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了携悯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筷笨,死狀恐怖憔鬼,靈堂內(nèi)的尸體忽然破棺而出龟劲,到底是詐尸還是另有隱情,我是刑警寧澤轴或,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布昌跌,位于F島的核電站,受9級(jí)特大地震影響照雁,放射性物質(zhì)發(fā)生泄漏蚕愤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一饺蚊、第九天 我趴在偏房一處隱蔽的房頂上張望萍诱。 院中可真熱鬧,春花似錦污呼、人聲如沸裕坊。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)籍凝。三九已至,卻和暖如春悟狱,著一層夾襖步出監(jiān)牢的瞬間静浴,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工挤渐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苹享,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓浴麻,卻偏偏與公主長(zhǎng)得像得问,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子软免,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 1.為什么會(huì)有跨域膏萧? 隨著軟件開(kāi)發(fā)分工趨于精細(xì)漓骚,前后端開(kāi)發(fā)分離成為趨勢(shì),前端同事負(fù)責(zé)前端頁(yè)面的展示及頁(yè)面邏輯處...
    b06ee9db5ac0閱讀 554評(píng)論 0 0
  • 本節(jié)內(nèi)容:實(shí)現(xiàn)跨域常用的兩種方式 —— JSONP 和 CORS 零:跨域報(bào)錯(cuò)展示 在非同源情況下榛泛,調(diào)用 Ajax...
    NathanYangcn閱讀 3,240評(píng)論 2 12
  • Section1蝌蹂、為什么要跨域? 自古以來(lái)(1995年起)曹锨,為了用戶的信息安全孤个,瀏覽器就引入了同源策略。那么同源策...
    不去解釋閱讀 549評(píng)論 0 0
  • Section1沛简、為什么要跨域齐鲤? 自古以來(lái)(1995年起)斥废,為了用戶的信息安全,瀏覽器就引入了同源策略给郊。那么同源策...
    qhaobaba閱讀 380評(píng)論 0 0
  • 越忙越亂牡肉,還是越忙越有條理? 不知從什么時(shí)候開(kāi)始丑罪,忙荚板,成了上班族推掉所有聚會(huì)出游的托詞。忙的狀態(tài)下吩屹,緊迫卻有條理的...
    紅泥小火爐閱讀 251評(píng)論 0 1