同源策略
同源策略(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