官方的說法:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。html
可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。web
說的直白一點中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。ajax
咱們一直都在使用中間件,只是沒有注意到而已,打開Django項目的Settings.py文件,看到下圖的MIDDLEWARE配置項。數據庫
MIDDLEWARE = [ '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', ]
MIDDLEWARE配置項是一個列表(列表是有序的,記住這一點,後面你就知道爲何要強調有序二字),列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。django
所以:只要之後用django開發業務 設計到全局相關的功能 就考慮用中間件來完成,好比下面這些flask
全局用戶身份校驗瀏覽器
全局用戶訪問頻率校驗安全
用戶訪問黑名單cookie
用戶訪問白名單session
自定義中間件咱們須要去看一眼源碼,找一下共同點
注意:找哪個中間件的源碼,咱們須要將其導出來才能看到,好比SessionMiddleware
from django.contrib.sessions.middleware import SessionMiddleware
共同點:
class SessionMiddleware(MiddlewareMixin): def process_request(self, request): def process_response(self, request, response): class CsrfViewMiddleware(MiddlewareMixin): def process_request(self, request): def process_view(self, request, callback, callback_args, callback_kwargs): def process_response(self, request, response): class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request):
django運行用戶自定義中間件而且暴露給用戶五個能夠自定義的方法
process_request(self, request): 請求過來的時候會按照配置文件中中間件從上到下的順序依次執行每個中間件中的process_request方法,若是沒有直接跳到下一個 若在定義中間件的process_request直接返回一個HttpResponse之類的則直接跳過剩下的部分從當前中間件的porcess_response執行後直接返回 注意:Django是同級別直接返回,而flask是遇到這種狀況會執行全部的process_response process_response 響應走的時候會按照配置文件中註冊的中間件從下往上的順序依次執行每個中間件裏面的 process_response方法 該方法必需要有兩個形參 而且須要將形參response返回 若是你內部本身返回了HttpResponse對象 會將返回給用戶瀏覽器的內容替換成你本身的
示例:
新建一個自定義中間件文件夾,在這個文件夾下新建一個自定義中間件的py文件
from django.utils.deprecation import MiddlewareMixin class Mymid1(MiddlewareMixin): def process_request(self, request): print('我是第一個自定義中間件的process_request') 如有同級別的返回則直接跳過剩下的部分直接返回 # return HttpResponse('一個返回值') def process_response(self, request, response): print('我是第一個自定義中間件的process_response') return response class Mymid2(MiddlewareMixin): def process_request(self, request): print('我是第二個自定義中間件的process_request') def process_response(self, request, response): print('我是第二個自定義中間件的process_response') return response
而後在配置文件中的MIDDLEWARE中將自定義的中間件路徑註冊上
結果:
# process_view方法 def process_view(self, request, view_func, view_args, view_kwargs): print('我是第一個中間件裏面的process_view')
該方法有四個參數
request是HttpRequest對象。 view_func是Django即將使用的視圖函數。 (它是實際的函數對象,而不是函數的名稱做爲字符串。) view_args是將傳遞給視圖的位置參數的列表. view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。
它應該返回None或一個HttpResponse對象。 若是返回None,Django將繼續處理這個請求,
執行任何其餘中間件的process_view方法,而後在執行相應的視圖。 若是它返回一個HttpResponse對象,
那麼將不會執行Django的視圖函數,而是直接在中間件中掉頭,倒敘執行一個個process_response方法,最後返回給瀏覽器
總結:Django會在調用視圖函數以前調用process_view方法。
def process_exception(self,request,exception): print('exception:',exception) print('我是第一個中間件裏面的process_exception') 該方法兩個參數: 一個HttpRequest對象 一個exception是視圖函數異常產生的Exception對象。 這個方法只有在視圖函數中出現異常了才執行,
它返回的值能夠是一個None也能夠是一個HttpResponse對象。
若是是HttpResponse對象,Django將調用模板和中間件中的process_response方法,
並返回給瀏覽器,不然將默認處理異常。若是返回一個None,則交給下一個中間件的process_exception方法來
總結:這個方法只有在視圖函數中出現異常了才執行
# 自定義中間件方法 def process_template_response(self,request,response): print('我是第一個中間件裏面的process_template_reponse方法') return response
# views def index(request): print('我是視圖函數index') def render(): return HttpResponse("你好啊 我是index裏面的render函數") obj = HttpResponse("index") obj.render = render return obj
總結:process_template_response(self, request, response) 它的參數,一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)。 process_template_response是在視圖函數執行完成後當即執行,可是它有一個前提條件,那就是視圖函數返回的對象有一個render屬性對應的render方法(或者代表該對象是一個TemplateResponse對象或等價方法)。
瞭解一下基本的釣魚網站
釣魚網站 本質搭建一個跟正常網站如出一轍的頁面 用戶在該頁面上完成轉帳功能 轉帳的請求確實是朝着正常網站的服務端提交 惟一不一樣的在於收款帳戶人不一樣 給用戶書寫form表單 對方帳戶的input沒有name屬性 你本身悄悄提早寫好了一個具備默認的而且是隱藏的具備name屬性的input
模擬真實網站和釣魚網站
先將django配置文件裏面的'django.middleware.csrf.CsrfViewMiddleware'
註釋了
# 真實網站 # views.py def transfer(request): if request.method == 'POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get('money') print('%s 給 %s 轉了 %s元' %(username, target_user, money)) return render(request, 'transfer.html') # transfer.html <h2>我是真實的網站</h2> <form action="" method="post"> <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text" name="target_user"></p> <p>money<input type="text" name="money"></p> <input type="submit"> </form> # 釣魚網站 # views.py def transfer(request): if request.method == 'POST': username = request.POST.get('username') target_user = request.POST.get('target_user') money = request.POST.get("money") print('%s 給 %s 轉了 %s元'%(username,target_user,money)) return render(request,'transfer.html') # transfer.html <h2>我是釣魚網站</h2> # 將傳輸數據的網站改成真實網站網址 <form action="http://127.0.0.1:8000/transfer/" method="post"> <p>username:<input type="text" name="username"></p> <p>target_user:<input type="text"></p> # 在轉帳人下面新建一個隱藏的input框,默認值爲jason,這樣上面這個框就失效了 <input type="text" name="target_user" style="display: none" value="jason"> <p>money:<input type="text" name="money"></p> <input type="submit"> </form>
這樣以上述代碼就能實現他人用釣魚網站來非法獲取你的轉帳信息並加以修改,那麼如何改變這種狀況
# 只要在上面的form表單中加上下面這個{% csrf_token %},就能夠啓動django配置中的django.middleware.csrf.CsrfViewMiddleware中間件,這樣每次訪問服務端都會隨機產生一個字符串,用於識別身份(注意是沒有絕對安全的web的) {% csrf_token %}
form表單經過csrf校驗
{% csrf_token %}
ajax經過csrf校驗
# 寫一個ajax請求 <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'jason'}, success:function (data) { alert(data) } }) }) </script>
第一種方法:手動獲取
<script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'jason','csrfmiddlewaretoken':$('input[name="csrfmiddlewaretoken"]').val()}, success:function (data) { alert(data) } }) }) </script>
第二種方法:利用模板語法(本地寫小項目推薦)
<script> $('d1').click(fnction(){ $.ajax({ url:'', type:'post', data:{'username':'jason','csrfmiddlewaretoken':''{{csrf_token}}''}, success:function(data){ alert(data) } }) }) </script>
第三種方法:寫一個靜態配置js文件引用過來(官網提供的,推薦使用,重點是知道複製第三步裏面的js文件內容)
#第一步,配置靜態文件 設置一個static文件夾 #第二步,在settings文件中配置靜態文件路徑 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'satic') ] # 第三步,在static文件夾下新建一個配置js文件,內容以下 function getCookie(name) { var cookieValue = null; if (document.cookie && document.cookie !== '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = jQuery.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({ beforeSend: function (xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); #第四步,在ajax請求中引入這個js文件 {% load static %} <script src="{% static 'myset.js' %}"></script> <script> $('#d1').click(function () { $.ajax({ url:'', type:'post', data:{'username':'jason'}, success:function (data) { alert(data) } }) }) </script>
FBV設置csrf裝飾器
導入模塊
from django.views.decorators.csrf import csrf_exempt, csrf_protect
當咱們網站總體都校驗csrf的時候 我想讓某幾個視圖函數不校驗
# 在指定視圖函數上面添加下面這個裝飾器 @csrf_exempt
當咱們網站總體都不校驗csrf的時候 我想讓某幾個視圖函數校驗
# 能夠將中間件 'django.middleware.csrf.CsrfViewMiddleware',註釋掉 # 而後在指定視圖函數上面添加下面這個裝飾器 @csrf_protect
如何將CBV設置csrf裝飾器
導入模塊
from django.views.decorators.csrf import csrf_exempt, csrf_protect from django.utils.decorators import method_decorator # 不管是本身寫的裝飾器仍是別人寫好的裝飾器都要記得用這個method_decorator方法
回憶一下CBV如何寫的
from django.views import View class MyHome(View): def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post') 注意在urls.py文件中的寫法 url(r'^home/',views.MyHome.as_view()),
如何讓某個方法添加上csrf功能
@method_decorator(csrf_protect, name=post) # 第一種方法:指定某個方法名添加csrf class MyHome(View): def get(self,request): return HttpResponse('get') @method_decorator(csrf_protect) #第二種方法:直接在方法名上添加裝飾器 def post(self,request): return HttpResponse('post')
from django.views import View from django.utils.decorators import method_decorator @method_decorator(csrf_protect,name='dispatch') # 第三種方法這樣寫也能夠 class MyHome(View): @method_decorator(csrf_protect) # 第三種方法 類中全部的方法都裝 def dispatch(self, request, *args, **kwargs): return super().dispatch(request,*args,**kwargs) def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post')
補充注意:上面的方法都是添加csrf校驗,對於如何指定某個CBV中方法不校驗,上面的一二兩種方法都不行,只能經過第三種方法給dispatch裝,將裏面的csrf_protect改成csrf_exempt便可
直接進行數據庫遷移命令咱們能夠看到數據庫中自行建立的一些表格
建立超級用戶
第一步:點擊快捷方式
在控制檯輸入命令
createsuperuser
建立成功的樣子
django後臺管理界面
auth模塊註冊功能
# 第一步導入auth模塊和auth模塊中的User表 from django.contrib import auth from django.contrib.auth.models import User def register(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # User.objects.create(username=username,password=password) # 不能使用 密碼變成明文的了 User.objects.create_user(username=username,password=password) # 這樣就能夠了 # User.objects.create_superuser(username=username,password=password,email='123@qq.com') # 這種建立超級用戶的方式須要填入郵箱,不然報錯 return render(request,'register.html')
auth模塊其餘功能
# 登錄認證 def login(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 明文 # 數據庫校驗用戶名和密碼是否正確,這樣是用明文校驗是不對的 # User.objects.filter(username=username,password=password) # 經過auth的內置方法來校驗 user_obj = auth.authenticate(request,username=username,password=password) print(user_obj) """ 用戶名密碼正確返回的是用戶對象 錯誤返回None """ print(res.username) print(res.password) if user_obj: # 保存用戶登陸狀態 # request.session auth.login(request,user_obj) """ 只要執行了這一句話 以後在任意能夠獲取到request對象的地方 均可以經過request.user獲取到當前登陸的用戶對象 """ return HttpResponse("登陸成功") return render(request,'login.html') def get_user(request): print(request.user) """ 用戶登陸成功以後 request.user拿到的就是用戶對象 沒有登陸 獲取到的匿名用戶 """ print(request.user.is_authenticated()) # 返回的是布爾值 return HttpResponse("get_user") # 局部配置校驗用戶是否登陸裝飾器 # 先導入模塊 from django.contrib.auth.decorators import login_required # 若裏面不傳指定login_url參數就會導入到django內置的的登錄界面 #@login_required @login_required(login_url='/login/') # 這樣能夠跳轉到自定義的登錄界面 def xxx(request): return HttpResponse('xxx頁面') # @login_required @login_required(login_url='/login/') def yyy(request): return HttpResponse('yyy頁面') #@login_required @login_required(login_url='/login/') def zzz(request): return HttpResponse('zzz頁面') # 如何設置全局配置校驗用戶是否登陸裝飾器 # 在settings.py文件中寫入下面這個代碼 LOGIN_URL = '/login/' # 而後在指定視圖函數上只要寫下面這個就能夠看,不須要在指定url了 @login_required # 若同時有局部和全局配置,優先使用局部的 #如何進行密碼修改 # 首先必定是登錄狀態 @login_required def set_password(request): if request.method == 'POST': old_password = request.POST.get('old_password') new_password = request.POST.get('new_password') # 1 先校驗舊密碼是否正確 is_right = request.user.check_password(old_password) # print(is_right) 是布爾值 # 2 再去修改新密碼 if is_right: request.user.set_password(new_password) request.user.save() # 必定要save一下 不然數據庫無影響 return render(request,'set_password.html') # 如何註銷登錄狀態 @login_required def logout(request): auth.logout(request) return HttpResponse('註銷成功')
如何將默認設置的user表進行擴展
# 第一種方法能夠利用一對一表關係,就是新建一張表,與其是一對一關係,則兩張表就是一張表,實現了擴展字段功能 #第二種,類的繼承 # 在models.py文件中導入下面模塊(實際上原先的django默認設置的表都是來源於AbstracterUser,所以咱們也能夠繼承這個類來建立本身的表) from django.contrib.auth.models import User,AbstractUser # Create your models here. class Userinfo(AbstractUser): phone = models.BigIntegerField() avatar = models.FileField() # 擴展的字段 儘可能不要與原先表中的字段衝突 # 而後在配置文件寫上下面這個代碼 AUTH_USER_MODEL = 'app01.Userinfo' # AUTH_USER_MODEL = '應用名.表名' """ django就會將userinfo表來替換auth_user表 而且以前auth模塊全部的功能不變 參照的也是userinfo表 """