同源策略限制了從同一個源架子啊的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。css
同源策略是瀏覽器的一個安全功能,不一樣源的客戶端腳本在沒有明確受權的狀況下,不能讀寫對方資源。html
若是兩個頁面的協議,端口(若是有指定)和主機都相同,則兩個頁面具備相同的源。前端
JSONP的簡單實現模式,或者說是JSONP的原型:建立一個回調函數,而後在遠程服務上調用這個函數而且將JSON 數據形式做爲參數傳遞,完成回調。jquery
經過script標籤來實現。ajax
後端代碼django
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): return HttpResponse("rion()")
前端代碼json
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">點我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> function rion() { console.log("盤他,盤圓潤嘍!"); } </script> <script src="http://127.0.0.1:8002/abc/"></script> </body> </html>
這樣就能夠簡單實現跨域,前端打印結果爲盤他,盤圓潤嘍!segmentfault
一樣的,這種方式也是能夠傳參數的。後端
後端代碼api
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} return HttpResponse("rion({})".format(json.dumps(res)))
前端代碼
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">點我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> function rion(res) { console.log(res); } </script> <script src="http://127.0.0.1:8002/abc/"></script> </body> </html>
這樣的話就能夠實現傳參的跨域。
後端代碼
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} func = request.GET.get("callback") return HttpResponse("{}({})".format(func, json.dumps(res)))
前端代碼
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">點我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> function rion(res) { console.log(res); } function addScriptTag(src){ var scriptEle = document.createElement("script"); $(scriptEle).attr("src", src); $("body").append(scriptEle); $(scriptEle).remove(); } $("#b1").click(function () { addScriptTag("http://127.0.0.1:8002/abc/") }) </script> </body> </html>
點擊b1按鈕的時候,會在頁面上插入一個script標籤,而後從後端獲取數據。
後端代碼
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} func = request.GET.get("callback") return HttpResponse("{}({})".format(func, json.dumps(res)))
前端代碼
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">點我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#b1").click(function () { $.getJSON("http://127.0.0.1:8002/abc/?callback=?", function (res) { console.log(res); }) }); </script> </body> </html>
要注意的是在url的後面必需要有一個callback參數,這樣getJSON方法纔會知道是用JSONP方式去訪問服務,callback後面的那個?是jQuery內部自動生成的一個回調函數名。
若是咱們想本身指定回調函數名,或者說服務上規定了回調函數名該怎麼辦呢?咱們能夠使用$.ajax方法來實現:
後端代碼
urlpatterns = [ url(r'^abc/', views.abc), ] def abc(request): res = {"code": 0, "data": ["SNIS-561", "SNIS-517", "SNIS-539"]} func = request.GET.get("callback") return HttpResponse("{}({})".format(func, json.dumps(res)))
前端代碼
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">點我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#b1").click(function () { $.ajax({ url: "http://127.0.0.1:8002/abc/", dataType: "jsonp", jsonp: "callback", jsonpCallback: "rion2" }) }); function rion2(res) { console.log(res); } </script> </body> </html>
不過,咱們一般將回調函數鞋子啊success回調中:
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>xyz</title> </head> <body> <button id="b1">點我</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#b1").click(function () { $.ajax({ url: "http://127.0.0.1:8002/abc/", dataType: "jsonp", success: function (res) { console.log(res); } }) }) </script> </body> </html>
CORS,跨域資源共享。CORS有兩種請求,簡單請求(simple request)和非簡單請求(no simple request)。
只要同時知足如下兩大條件,就屬於簡單請求。
請求方法是如下三種之一:
HTTP的頭信息不超出如下幾種字段:
在跨域場景下,當瀏覽器發送簡單請求時,瀏覽器會自動在請求頭中添加代表請求來源的 Origin 字段。
後端程序只須要在返回的響應頭中加上 Access-Control-Allow-Origin 字段,而且把該字段的值設置爲 跨域請求的來源地址或簡單的設置爲 * 就能夠了。
能夠在Django中間件中的process_response方法來給相應對象添加該字段。
from django.utils.deprecation import MiddlewareMixin class CorsMiddleware(MiddlewareMixin): def process_response(self, request, response): # 給響應頭加上 Access-Control-Allow-Origin 字段 並簡單的設置爲 * response['Access-Control-Allow-Origin'] = '*' return response
對於非簡單請求,瀏覽器一般都會在請求以前發送一次 OPTIONS 預檢 請求,返回碼是204,該請求會向後端服務詢問是否容許從當前源發送請求而且詢問容許的 請求方法 和 請求頭字段。預檢測經過纔會真正發出請求,這才返回200。
如今前端向後端發送PUT請求,具體的發送請求以下:
解決犯法就是在後端簡單的響應對象添加上經常使用的請求方法。
django代碼示例以下:
from django.utils.deprecation import MiddlewareMixin class CorsMiddleware(MiddlewareMixin): def process_response(self, request, response): # 給響應頭加上 Access-Control-Allow-Origin 字段 並簡單的設置爲 * response['Access-Control-Allow-Origin'] = '*' if request.method == 'OPTIONS': # 容許發送 PUT 請求 response['Access-Control-Allow-Methods'] = 'PUT, DELETE' # 容許在請求頭中攜帶 Content-type字段,從而支持發送json數據 response['Access-Control-Allow-Headers'] = 'Content-type' return response
這個是一個處理cors跨域問題的包,咱們只須要安裝使用便可。
安裝
pip install django-cors-headers
註冊APP
INSTALLED_APPS = [ ... 'app01.apps.App01Config', 'corsheaders', # 將 corsheaders 這個APP註冊 ]
添加中間件
必須放在最前面,由於要解決跨域問題,只有容許跨域請求了,後續中間價纔會正常執行。
MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # 添加中間件 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
配置
能夠選擇不限制跨域訪問
CORS_ORIGIN_ALLOW_ALL = True
也能夠設置容許訪問的白名單
CORS_ORIGIN_ALLOW_ALL = False CORS_ORIGIN_WHITELIST = ( # '<YOUR_DOMAIN>[:PORT]', '127.0.0.1:8080' )
若是咱們請求的時候仍是用前端的域名,而後有個東西幫咱們把這個請求轉發到真正的後端域名上,這樣就避免跨域,這時候,Nginx出場了。
server { # 監聽9099端口 listen 9099; # 域名是localhost server_name localhost; # 凡是localhost:9099/api這樣子的,都轉發到真正的服務端地址http://localhost:9871 location ^~ /api { proxy_pass http://localhost:9871; } }
參看資料
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy