若是兩個頁面的協議,端口(若是有指定)和域名都相同,則兩個頁面具備相同的源。
舉個例子:javascript
下表給出了相對http://a.xyz.com/dir/page.html同源檢測的示例: URL 結果 緣由 http://a.xyz.com/dir2/other.html 成功 協議,端口(若是有指定)和域名都相同 http://a.xyz.com/dir/inner/another.html 成功 協議,端口(若是有指定)和域名都相同 https://a.xyz.com/secure.html 失敗 不一樣協議 ( https和http ) http://a.xyz.com:81/dir/etc.html 失敗 不一樣端口 ( 81和80) http://a.opq.com/dir/other.html 失敗 不一樣域名 ( xyz和opq)
同源策略是瀏覽器的一個安全功能,不一樣源的客戶端腳本在沒有明確受權的狀況下,不能讀寫對方資源。因此xyz.com下的js腳本採用ajax讀取abc.com裏面的文件數據是會被拒絕的。css
同源策略限制了從同一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。html
頁面中的連接,重定向以及表單提交是不會受到同源策略限制的。java
跨域資源的引入是能夠的。可是js不能讀寫加載的內容。如嵌入到頁面中的<script src="..."></script>,<img>,<link>,<iframe>等。jquery
下面來分步舉例詳細闡述其中的奧妙:ajax
項目1(http://127.0.0.1:8000/)
項目2(http://127.0.0.1:8100/)django
項目1json
url: url(r'index1/$',views.index1) views: def index1(request): return HttpResponse('wangjifei')
項目2後端
url: url(r'index2/$',views.index2) views : def index2(request): return render(request,'index2.html') index2.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> $('#btn').click(function () { $.ajax({ url:"http://127.0.0.1:8000/index1/", type:'get', success:function (res) { console.log(res) } }) }) </script> </body> </html>
如今,打開使用瀏覽器打開 http://127.0.0.1:8100/index2/,點擊頁面上的 '提交' 按鈕,會在console頁面發現錯誤信息以下:跨域
爲何報錯呢?由於同源策略限制跨域發送ajax請求。
細心點的同窗應該會發現咱們的demo1項目其實已經接收到了請求並返回了響應,是瀏覽器對非同源請求返回的結果作了攔截。
再細心點的同窗會發現,咱們使用cdn方式引用的jQuery文件也是跨域的,它就可使用。
一樣是從其餘的站點拿東西,script標籤就能夠。那咱們能不能利用這一點搞點事情呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
如今刷新一下會出現以下錯誤:
看來後端返回的響應已經被拿到了,只不過把wangjifei當成了一個變量來使用,可是該頁面上卻沒有定義一個名爲wangjifei的變量。因此出錯了。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script> var wangjifei = 123 </script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
刷新發現不報錯了,
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script> function wangjifei() { console.log('出手就要專業') } </script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
項目1中的views:也修改一下
def index1(request): return HttpResponse('wangjifei()')
刷新一下頁面顯示結果:
結果分析:返回的 wangjifei(),頁面上拿到這個響應以後直接執行了wangjifei函數!
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script> function wangjifei(res) { console.log(res) } </script> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script src="http://127.0.0.1:8000/index1/"></script> </body> </html>
項目1中的 views
from django.http import HttpResponse import json def index1(request): ret={'code':1,'msg':[110,119,120,12306]} res = json.dumps(ret) return HttpResponse(f'wangjifei({res})')
刷新以後顯示結果:
果真傳遞參數也是能夠的!咱們經過script標籤的跨域特性來繞過同源策略拿到想要的數據了!!!
這其實就是JSONP的簡單實現模式,或者說是JSONP的原型:建立一個回調函數,而後在遠程服務上調用這個函數而且將JSON 數據形式做爲參數傳遞,完成回調。
將JSON數據填充進回調函數,這就是JSONP的JSON+Padding的含義。
可是咱們更多時候是但願經過事件觸發數據的獲取,而不是像上面同樣頁面一刷新就執行了,這樣很不靈活。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //自定義的函數 function wangjifei(res) { console.log(res) } //jquery給button綁定點擊事件 $('#btn').click(function () { //建立一個script標籤 var scriptEle = document.createElement('script'); //給標籤添加src屬性,並添加對應的屬性值 http://127.0.0.1:8000/index1 $(scriptEle).attr('src','http://127.0.0.1:8000/index1'); //將建立好的標籤添加到頁面中,標籤添加後就會自動觸發get請求 $('body').append(scriptEle); //將標籤移除 $(scriptEle).remove() }) </script> </body> </html>
這樣當咱們點擊button按鈕的時候,會在頁面上插入一個script標籤,而後從後端獲取數據後再刪除掉。
index2.html代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //自定義的函數 function xxx(res) { console.log(res) } //jquery給button綁定點擊事件 $('#btn').click(function () { //建立一個script標籤 var scriptEle = document.createElement('script'); //給標籤添加src屬性,並添加對應的屬性值 http://127.0.0.1:8000/index1?callback=xxx $(scriptEle).attr('src','http://127.0.0.1:8000/index1?callback=xxx'); //將建立好的標籤添加到頁面中,標籤添加後就會自動觸發get請求 $('body').append(scriptEle); //將標籤移除 $(scriptEle).remove() }) </script> </body> </html>
項目1中views:
from django.http import HttpResponse import json def index1(request): ret={'code':1,'msg':[110,119,120,12306]} res = json.dumps(ret) callback = request.GET.get('callback') return HttpResponse(f'{callback}({res})')
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //jquery給button綁定點擊事件 $('#btn').click(function () { $.getJSON("http://127.0.0.1:8000/index1?callback=?",function (res) { console.log(res) }) }) </script> </body> </html>
要注意的是在url的後面必需要有一個callback參數,這樣getJSON方法纔會知道是用JSONP方式去訪問服務,callback後面的那個?是jQuery內部自動生成的一個回調函數名。
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //jquery給button綁定點擊事件 $('#btn').click(function () { $.ajax({ //要訪問的url url:"http://127.0.0.1:8000/index1/", //要處理的數據類型jsonp dataType:'jsonp', //自定義回調函數名必要參數 jsonp:'callback', //自定義回調函數名,url中callback=後面的函數名 jsonpcallback:'wangjifei' }) }); //回調函數 function wangjifei(res) { console.log(res) } </script> </body> </html>
views:
from django.http import HttpResponse import json def index1(request): ret={'code':1,'msg':[110,119,120,12306]} res = json.dumps(ret) callback = request.GET.get('callback') return HttpResponse(f'wangjifei({res})')
index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="btn">提交</button> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"> </script> <script> //jquery給button綁定點擊事件 $('#btn').click(function () { $.ajax({ //要訪問的url url:"http://127.0.0.1:8000/index1/", //要處理的數據類型jsonp dataType:'jsonp', //success回調 success:function (res) { console.log(res) } }) }); //回調函數 function wangjifei(res) { console.log(res) } </script> </body> </html>
最後來一個jsonp的實際應用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>同源策略</title> </head> <body> <button id="show-tv">提交</button> <div class="tv-list"></div> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> $("#show-tv").click(function () { $.ajax({ url: "http://www.jxntv.cn/data/jmd-jxtv2.html? callback=list&_=1454376870403", dataType: 'jsonp', jsonp: 'callback', jsonpCallback: 'list', success: function (data) { var weekList = data.data; console.log(weekList); var $tvListEle = $(".tv-list"); $.each(weekList, function (k, v) { var s1 = "<p>" + v.week + "列表</p>"; $tvListEle.append(s1); $.each(v.list, function (k2, v2) { var s2 = "<p><a href='" + v2.link + "'>" + v2.name + "</a></p>"; $tvListEle.append(s2) }); $tvListEle.append("<hr>"); }) } }) }); </script> </body> </html>
咱們介紹了jsonp解決跨域請求問題,這種解決方式很好的詮釋了跨域請求的本質,可是略顯麻煩,是否還記得在咱們不作任何處理的時候,跨域請求時候瀏覽器給咱們報的錯誤不?翻譯過來就是由於響應頭沒有指定Access-Control-Allow-Origin所容許原始的請求路徑,所以原始請求路徑http://127.0.0.1:8001不被容許訪問。 基於上述的緣由解釋,咱們只須要在響應的內容加入上述這樣的受權字段,即可解決。
簡單請求的定義:
只要同時知足如下兩大條件,就屬於簡單請求,不知足就是複雜請求!!!
1.(1) 請求方法是如下三種方法之一:-- HEAD,GET,POST
2.(2)HTTP的頭信息不超出如下幾種字段:-- Accept
-- Accept-Language
-- Content-Language
-- Last-Event-ID
-- Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
因爲django的全部請求響應都要走中間件,因此能夠寫一個跨域的中間件來解決跨域問題
from django.utils.deprecation import MiddlewareMixin class MyCore(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = "*" //簡單請求 if request.method == "OPTIONS": # 複雜請求 預檢 response['Access-Control-Allow-Headers'] = "Content-Type" response['Access-Control-Allow-Methods'] = "POST, DELETE, PUT" return response