Django的中間件相似因而Django的保安html
請求來的時候須要先通過中間件才能到達Django的後端前端
響應走的時候也須要通過中間件才能到達web服務網關接口web
官方的說法:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。ajax
可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。數據庫
說的直白一點中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。django
中間件在Django中就位於settings.py文件中的MIDDLEWARE配置項後端
# 下面這幾行代碼就是Django的中間件 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配置項是一個列表(列表是有序的,記住這一點,後面你就知道爲何要強調有序二字),列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。瀏覽器
咱們以前已經接觸過一個csrf相關的中間件了?咱們一開始讓你們把他註釋掉,再提交post請求的時候,就不會被forbidden了,咱們學會使用csrf_token以後就再也不註釋這個中間件了。cookie
Django中間件能夠用來作什麼session
1.網站全集的身份校驗,訪問頻率限制,權限校驗,涉及到全局的校驗均可以在中間件中完成
2.Django的中間件是全部web框架中作的最好的
中間件能夠定義五個方法,分別是:(主要的是process_request和process_response)
process_request(self,request)
process_view(self,request,view_func,view_args,view_kwargs)
process_template_response(self,request,response)
process_exception(self,request,exception)
process_response(self,request,response)
以上方法的返回值能夠是None或一個HttpResponse對象,若是是None,則繼續按照django定義的規則向後繼續執行,若是是HttpResponse對象,則直接將該對象返回給用戶。
如何自定義咱們本身的中間件
1.若是想讓本身寫的中間件生效,就必須先繼承MiddlewareMixin
2.在註冊自定義中間件的時候,必定要確保路徑不能寫錯
process_request(self,request)方法和process_response(self,request,response)方法
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse class MyMdd(MiddlewareMixin): def process_request(self,request): print('我是第一個中間件裏的process_request方法') def process_response(self,request,response): print('我是第一個中間件裏的process_response方法') return response class MyMdd1(MiddlewareMixin): def process_request(self,request): print('我是第二個中間件裏的process_request方法') return HttpResponse('第二個中間件就返回了') def process_response(self,request,response): print('我是第一個中間件裏的process_response方法') return response class MyMdd2(MiddlewareMixin): def process_request(self,request): print('我是第三個中間件裏的process_request方法') def process_response(self,request,response): print('我是第一個中間件裏的process_response方法') return response
process_request
1.請求來的時候,會通過每一箇中間件裏面的process_request方法,從上往下
2.若是方法裏返回了HTTPResponse對象,那麼會直接返回,再也不往下執行
基於該特色就能夠作訪問頻率限制,身份校驗,權限校驗等
class MyMdd(MiddlewareMixin): def process_request(self,request): print('我是第一個中間件裏的process_request方法') class MyMdd1(MiddlewareMixin): def process_request(self,request): print('我是第二個中間件裏的process_request方法') class MyMdd2(MiddlewareMixin): def process_request(self,request): print('我是第三個中間件裏的process_request方法')
當在某個process_request方法直接返回了HttpResponse對象就不會繼續執行了
# 第一個中間件就返回了 class MyMdd(MiddlewareMixin): def process_request(self,request): print('我是第一個中間件裏的process_request方法') return HttpResponse('第一個中間件就返回了')
process_response
1.必須將response形參返回,由於這個形參指代的就是要返回給前端的數據
2.響應走的時候,會依次通過每個中間件裏面的process_response方法,從下往上
# process_response方法 class MyMdd(MiddlewareMixin): def process_request(self,request): print('我是第一個中間件裏的process_request方法') def process_response(self,request,response): print('我是第一個中間件裏的process_response方法') return response class MyMdd1(MiddlewareMixin): def process_request(self,request): print('我是第二個中間件裏的process_request方法') def process_response(self,request,response): print('我是第二個中間件裏的process_response方法') return response class MyMdd2(MiddlewareMixin): def process_request(self,request): print('我是第三個中間件裏的process_request方法') def process_response(self,request,response): print('我是第三個中間件裏的process_response方法') return response
process_view()
在路由匹配成功以後,執行視圖函數以前觸發
class MyMdd(MiddlewareMixin): def process_view(self,request,view_func,view_args,view_kwargs): print(view_func) print(view_args) print(view_kwargs) print('我是第一個中間件裏的process_view方法') ...
view_func會拿到視圖函數名
process_exception()
當視圖函數報錯時,自動執行
class MyMdd(MiddlewareMixin): def process_exception(self,request,exception): print('我是第一個中間件裏的process_exception方法') ...
process_template_response()
當你返回的HTTPResponse對象中必須包含render屬性纔會觸發
# 視圖函數層 def index(request): print('我是index函數') def render(): return HttpResponse('包含render屬性') obj = HttpResponse('index') obj.render = render return obj
class MyMdd(MiddlewareMixin): def process_template_response(self,request,response): print('我是第一個中間件裏的process_template_response方法') return response ...
總結:在書寫中間件的時候,只要形參中有response,就須要將其返回,這個response就是給前端的響應信息
釣魚網站
經過製做一個跟正兒八經的網站如出一轍的頁面,騙取用戶輸入信息 轉帳交易,從而作手腳,轉帳交易的請求確確實實是發給了中國銀行,帳戶的錢也是確確實實少了,惟一不同的地方在於收款人帳戶不對
內部原理
在讓用戶輸入對方帳戶的那個input上面作手腳,給這個input不設置name屬性,在內部隱藏一個實現寫好的name和value屬性的input框,這個value的值 就是釣魚網站受益人帳號
<h1>這是真的網站</h1> <form action="" method="post"> <p>本人:<input type="text" name="username"></p> <p>對方帳戶:<input type="text" name="target_username"></p> <p>金額:<input type="text" name="price"></p> <input type="submit"> </form>
<h1>這是釣魚網站</h1> <form action="http://127.0.0.1:8000/transfer/" method="post"> <p>本人:<input type="text" name="username"></p> <p>對方帳戶:<input type="text"></p> <p><input type="hidden" name="target_username" value="sxc"></p> <p>金額:<input type="text" name="price"></p> <input type="submit"> </form>
防止釣魚網站的思路
真實網站經過返回給用戶的form表單頁面偷偷的塞一個隨機字符串
當請求到來的時候,會先比對隨機字符串是否一致,若是不一致,直接返回403頁面
該隨機字符串有如下特色
1.同一個瀏覽器每次訪問都不同
2.不一樣瀏覽器絕對不會重複
1.form表單發送post請求的時候,須要在form表單里加上一句話
{% csrf_token %}
這時候csrf中間件須要打開才能正確的驗證
'django.middleware.csrf.CsrfViewMiddleware',
2.ajax發送post請求,避免csrf校驗
一共有三種方式
1.先在頁面上寫{% csrf_token %}利用標籤查找,獲取到該input標籤的鍵值信息
// 第一種方式 data:{'username':'sxc','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()},
2.直接書寫'{{ csrf_token }}'
// 第二種方式 data:{'username':'sxc','csrfmiddlewaretoken':'{{ csrf_token }}'},
3.導入js文件,將獲取隨機鍵值對的方法寫到該js文件中
首先新建一個js文件,存放如下代碼,以後導入該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); } } });
在html頁面直接引入該js文件便可
{% load static %} <script src="{% static 'setjs.js' %}"></script>
1.當你網站全局都須要校驗csrf的時候 有幾個不須要校驗該如何處理
from django.views.decorators.csrf import csrf_exempt,csrf_protect # 導入模塊後直接裝飾在函數前 @csrf_exempt def login(request): return HttpResponse('login')
2.當你網站全局不校驗csrf的時候 有幾個須要校驗又該如何處理
# 裝飾在函數前 @csrf_protect def lll(request): return HttpResponse('lll')
1.當你網站全局都須要校驗csrf的時候 有幾個不須要校驗該如何處理
from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt,csrf_protect # 這兩個裝飾器在給CBV裝飾的時候 有必定的區別 若是是csrf_protect 那麼有三種方式 # 第一種方式 # @method_decorator(csrf_protect,name='post') # 有效的 class MyView(View): # 第三種方式 # @method_decorator(csrf_protect) def dispatch(self, request, *args, **kwargs): res = super().dispatch(request, *args, **kwargs) return res def get(self,request): return HttpResponse('get') # 第二種方式 # @method_decorator(csrf_protect) # 有效的 def post(self,request): return HttpResponse('post')
若是是csrf_exempt 只有兩種(只能給dispatch裝) 特例
@method_decorator(csrf_exempt,name='dispatch') # 第二種能夠不校驗的方式 class MyView(View): # @method_decorator(csrf_exempt) # 第一種能夠不校驗的方式 def dispatch(self, request, *args, **kwargs): res = super().dispatch(request, *args, **kwargs) return res def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post')
總結:csrf裝飾器中只有crsf_exempt是特例,其餘的裝飾器在給CBV裝飾時均可以有三種方式
Auth模塊是Django自帶的用戶認證模塊:
咱們在開發一個網站的時候,無可避免的須要設計實現網站的用戶系統。此時咱們須要實現包括用戶註冊、用戶登陸、用戶認證、註銷、修改密碼等功能,這還真是個麻煩的事情呢。
Django做爲一個完美主義者的終極框架,固然也會想到用戶的這些痛點。它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據。
導入模塊
from django.contrib import auth
auth模塊的功能
查詢用戶 from django.contrib import auth user_obj = auth.authenticate(username=username,password=password) # 必需要用 由於數據庫中的密碼字段是密文的 而你獲取的用戶輸入的是明文 記錄用戶狀態 auth.login(request,user_obj) # 將用戶狀態記錄到session中 判斷用戶是否登陸 print(request.user.is_authenticated) # 判斷用戶是否登陸 若是是大家用戶會返回False 用戶登陸以後 獲取用戶對象 print(request.user) # 若是沒有執行auth.login那麼拿到的是匿名用戶 校驗用戶是否登陸 from django.contrib.auth.decorators import login_required @login_required(login_url='/xxx/') # 局部配置 def index(request): pass # 全局配置 settings文件中 LOGIN_URL = '/xxx/' 驗證密碼是否正確 request.user.check_password(old_password) 修改密碼 request.user.set_password(new_password) request.user.save() # 修改密碼的時候 必定要save保存 不然沒法生效 退出登錄 auth.logout(request) # request.session.flush() 註冊用戶 # User.objects.create(username =username,password=password) # 建立用戶名的時候 千萬不要再使用create 了 # User.objects.create_user(username =username,password=password) # 建立普通用戶 User.objects.create_superuser(username =username,password=password,email='123@qq.com') # 建立超級用戶 郵箱必填
這內置的認證系統這麼好用,可是auth_user表字段都是固定的那幾個,我在項目中無法拿來直接使用啊!
好比,我想要加一個存儲用戶手機號的字段,怎麼辦?
聰明的你可能會想到新建另一張表而後經過一對一和內置的auth_user表關聯,這樣雖然能知足要求可是有沒有更好的實現方式呢?
答案是固然有了。
咱們能夠經過繼承內置的 AbstractUser 類,來定義一個本身的Model類。
這樣既能根據項目需求靈活的設計用戶表,又能使用Django強大的認證系統了。
from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): """ 用戶信息表 """ nid = models.AutoField(primary_key=True) phone = models.CharField(max_length=11, null=True, unique=True) def __str__(self): return self.username
注意:
按上面的方式擴展了內置的auth_user表以後,必定要在settings.py中告訴Django,我如今使用我新定義的UserInfo表來作用戶認證。寫法以下:
# 引用Django自帶的User表,繼承使用時須要設置 AUTH_USER_MODEL = "app名.UserInfo"
再次注意:
一旦咱們指定了新的認證系統所使用的表,咱們就須要從新在數據庫中建立該表,而不能繼續使用原來默認的auth_user表了。
自定義auth_user表
from django.contrib.auth.models import AbstractUser # Create your models here. # 第一種 使用一對一關係 不考慮 # 第二種方式 使用類的繼承 class Userinfo(AbstractUser): # 千萬不要跟原來表中的字段重複 只能創新 phone = models.BigIntegerField() avatar = models.CharField(max_length=32) # 必定要在配置文件中 告訴django # 告訴django orm再也不使用auth默認的表 而是使用你自定義的表 AUTH_USER_MODEL = 'app01.Userinfo' # '應用名.類名' 1.執行數據庫遷移命令 全部的auth模塊功能 所有都基於你建立的表 而再也不使用auth_user
參考Django配置文件中間件的功能模塊
一旦註釋 >>> 該功能即不能用
一旦打開 >>> 該功能即啓用
咱們首先寫三個功能
class Msg(object): def __init__(self): pass # 發送短信須要的代碼配置 def send(self,content): print('短信通知:%s' % content)
class Email(object): def __init__(self): pass # 發送郵件須要的代碼配置 def send(self,content): print('郵件通知:%s'%content)
class QQ(object): def __init__(self): pass # 發送qq須要的代碼準備 def send(self,content): print('qq通知:%s'%content)
將這三個py文件放在一個文件夾中,這樣這個文件夾就成爲了一個包
咱們在配置文件中配置好三個功能的路徑
NOTIFY_LIST = [ 'notify.email.Email', 'notify.msg.Msg', # 'notify.wechat.WeChat', 'notify.qq.QQ', ]
在包中的__init__文件中書寫關鍵的代碼
import settings import importlib def send_all(content): for path_str in settings.NOTIFY_LIST: # 1.拿出一個個的字符串 'notify.email.Email' module_path,class_name = path_str.rsplit('.',maxsplit=1) # 2.從右邊開始 按照點切一個 ['notify.email','Email'] module = importlib.import_module(module_path) # from notity import msg,email,wechat cls = getattr(module,class_name) # 利用反射 一切皆對象的思想 從文件中獲取屬性或者方法 cls = 一個個的類名 obj = cls() # 類實例化生成對象 obj.send(content) # 對象調方法
關鍵代碼解析:
1.從settings中循環拿出每一個功能的路徑
2.解壓賦值,分紅路徑和類名
3.使用importlib經過字符串導入路徑
4.利用反射,經過類名字符串獲取類
5.實例化生成對象並調用對象的方法
在啓動文件中傳入參數就可使用這三個功能
import notify notify.send_all('國慶放假了 記住放八天哦')