django中間件就是相似於django的保安;請求來的時候須要先通過中間件,才能到達django後端(url,views,models,templates),css
響應走的的時候也須要通過中間件才能到達web服務器網關接口處;html
中間件位於web服務端與url路由層之間;是介於request與response處理之間的一道處理過程。前端
若是你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些均可以經過中間件來實現。python
一、網站全局的身份校驗,訪問頻率限制,權限檢驗等;只要涉及到全局校驗的均可以用中間件來實現jquery
二、Django的中間件是全部的web框架中作得最好的web
Django默認的中間件:(在django項目的settings模塊中,有一個MIDDLEWARE_CLASSES變量,其中的每個元素就是一箇中間件)ajax
中間件能夠定義五個方法;其中主要的是(process_request:請求 和process_response:返回)數據庫
一、process_request(self,request)
二、process_view(self, request, callback, callback_args, callback_kwargs)
三、process_template_response(self,request,response)
四、process_exception(self, request, exception)
五、process_response(self, request, response)
以上的方法的返回值能夠是None或一個HttpResponse對象,若是是None,繼續按照Django定義的規則向後繼續執行,django
若是是HttpResponse對象,則直接將該對象返回給用戶便可。編程
當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。
以下圖所示,一個完整的中間件的流程:
一、.process_request()方法
規律:
一、請求來的時候,會通過每一箇中間件裏面的process_request()方法(從上到下的順序)
二、若是返回的是HttpResponse對象,那麼會直接返回,再也不往下執行了;基於這一特色就能夠作訪問的頻率限制,身份校驗,權限校驗等
二、process_response()方法
規律:
(1)、必須將response形參返回,由於這個形參指代的就是要返回給前端的數據。
(2)、響應走的時候,會依次通過每個中間件裏面的process_response方法(從下往上)
須要瞭解的方法:
(1)、process_view() :
在路由匹配成功執行視圖函數以前 觸發
(2)、process_exception() :
當你的視圖函數報錯時 就會自動執行
(3)、process_template_response()
當你返回的HttpResponse對象中必須包含render屬性纔會觸發
from django.utils.deprecation import MiddlewareMixin
from django.utils.deprecation import MiddlewareMixin# from django.shortcuts import HttpResponse # class Md1(MiddlewareMixin): # def process_request(self,request): print("Md1請求") # def process_response(self,request,response): print("Md1返回") return response # class Md2(MiddlewareMixin): # def process_request(self,request): print("Md2請求") #return HttpResponse("Md2中斷") def process_response(self,request,response):# print("Md2返回") return response
def index(request): print("view函數...") return HttpResponse("OK")
1.若是你想讓你寫的中間件生效,就必需要先繼承MiddlewareMixin
2.在註冊自定義中間件的時候,必定要確保路徑不要寫錯
(6)若是沒有返回response形參,由於這個形參指代的就是要返回給前端的數據
報錯結果顯示:
該方法有四個參數
process_view(self, request, view_func, view_args, view_kwargs)
實例:
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse,redirect,render,reverse class Md1(MiddlewareMixin): def process_request(self,request): print('Md1請求') return HttpResponse("Md1中斷") def process_response(self,request,response): print('Md1返回') return response # return HttpResponse("嘿嘿!") def process_view(self,request,callback,callback_args,callback_kwargs): print('Md1views') class Md2(MiddlewareMixin): def process_request(self,request): print("Md2請求") return HttpResponse('Md2中斷') def process_response(self,request,response): print('Md2返回') return response def process_view(self,callback,callback_args,callback_kwargs): print("Md2views")
process_exception(self, request, exception)
一個HttpRequest對象
一個exception是視圖函數異常產生的Exception對象。
該方法對視圖函數返回值有要求,必須是一個含有render方法類的對象,纔會執行此方法
一、什麼是csrf
CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲「One Click Attack」或者Session Riding;
1.登陸受信任網站A,並在本地生成Cookie。
2.在不登出A的狀況下,訪問危險網站B。
正規的網站:
views.py
def transfer(request): if request.method == 'POST': username = request.POST.get('username') money = request.POST.get('money') target_user = request.POST.get('target_user') print('%s 給 %s 轉了 %s元'%(username,target_user,money)) return render(request,'res.html')
res.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> </head> <body> <h2>這是正兒八經的網站</h2> <form action="//" method="post"> {# {% csrf_token %}#} <p>本人用戶名:<input type="text" name="username"></p> <p>轉帳金額:<input type="text" name="money"></p> <p>對方帳戶:<input type="text" name="target_user"></p> <input type="submit"> </form>
在讓用戶輸入對方帳戶的那個input上面作手腳,寫一個同樣的viewx.py,和路由url,
修改前端HTML的內容讓轉帳對方的用戶隱藏起來綁定value=’jason‘,啓動時修改端口。
網站會給返回的用戶的form表單頁面,偷偷的噻一個隨機的字符串,請求來的時候,
會先比對隨機字符串是否一致,若是不一致,直接拒絕(403)
該隨機字符串有一下特色:
一、同一個瀏覽器沒一次訪問都不同
二、不一樣的瀏覽器之間絕對不會重複
一、form表發送post請求的時候,只須要書寫一句話便可
{% csrf_token %}
書寫{% csrf_token %},會在客戶端生成一對鍵值對
(1)、如今頁面上寫{% csrf_token %},利用標籤查找 ,獲取到該input鍵值信息,關鍵字:'csrfmiddlewaretoken'
{'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
(2)、直接書寫'{{ csrf_token }}'
{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}
(3)、你能夠將該獲取隨機鍵值對的方法,寫到一個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); } } });
1.當你網站全局都須要校驗csrf的時候 有幾個不須要校驗該如何處理?
2.當你網站全局不校驗csrf的時候 有幾個須要校驗又該如何處理?
全站禁用:註釋掉中間件 'django.middleware.csrf.CsrfViewMiddleware',
局部禁用:用裝飾器(在FBV中使用)
CBV比較特殊,不能單獨加在某個方法上;只能加在類上或dispatch方法上
from django.test import TestCase # Create your tests here. 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')
auth跟用戶相關的功能模塊:用戶的註冊、登陸、驗證、修改密碼等。
首先建立超級用戶:createsuperuser,這個超級用戶就能夠擁有登錄django admin後臺管理的權限
在建立超級用戶時:不可手動插入,由於密碼事加密的
這個超級用戶能夠登陸到Django後臺管理權限 :
基於這張表寫一個登陸的功能:
若是想用auth模塊,那就必須用全套,好比用戶的獲取和保存等auth.authenticate,後期就不能用session來保存
from django.contrib import auth # 必需要用 由於數據庫中的密碼字段是密文的 而你獲取的用戶輸入的是明文
查詢用戶,比較數據 user_obj = auth.authenticate(username=username,password=password)
記錄用戶狀態 auth.login(request,user_obj) # 將用戶狀態記錄到session中 判斷用戶是否登陸,用了auth。login 後就能夠用.屬性獲取 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/"
方法在用戶註冊登陸的簡單校驗的實際運用:
from django.contrib import auth def xxx(request): if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') # 取數據庫查詢當前用戶數據 # models.User.objects.filter(username=username,password=password).first() # 必需要用 由於數據庫中的密碼字段是密文的 而你獲取的用戶輸入的是明文 user_obj = auth.authenticate(username=username,password=password) print(user_obj) # print(user_obj) # print(user_obj.username) # print(user_obj.password) # 保存用戶狀態 # request.session['user'] = user_obj auth.login(request,user_obj) # 將用戶狀態記錄到session中 """只要執行了這一句話 你就能夠在後端任意位置經過request.user獲取到當前用戶對象""" return render(request,'xxx.html') def yyy(request): print(request.user) # 若是沒有執行auth.login那麼拿到的是匿名用戶 print(request.user.is_authenticated) # 判斷用戶是否登陸 若是是大家用戶會返回False # print(request.user.username) # print(request.user.password) return HttpResponse('yyy')
裝飾器校驗是否登錄及跳轉
from django.contrib.auth.decorators import login_required @login_required(login_url='/login/',redirect_field_name='old')
# 沒登錄會跳轉到login頁面,而且後面會拼接上你上一次想訪問的頁面路徑/login/?next=/test/,能夠經過參數修改next鍵名 def my_view(request): pass
若是我全部的視圖函數都須要裝飾並跳轉到login頁面,那麼我須要寫好多份
# 能夠在配置文件中指定auth校驗登錄不合法統一跳轉到某個路徑 LOGIN_URL = '/login/' # 既能夠局部配置,也能夠全局配置
回到最上面,咱們是怎麼對auth_user表添加數據的?命令行輸入~~~合理不?
from django.contrib.auth.models import User def register(request): User.objects.create() # 不能用這個,由於密碼是明文 User.objects.createuser() # 建立普通用戶 User.objects.createsuperuser() # 建立超級用戶
校驗密碼,修改密碼
request.user.check_password(pwd) # 爲何不直接獲取查,由於前端用戶輸入的是明文數據庫密文 request.user.set_password(pwd) request.user.save() # 修改密碼
自帶的登陸裝飾器:
from django.contrib.auth.decorators import login_required
具體的使用
from django.contrib.auth.decorators import 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') # 先判斷原密碼是否正確 # 將獲取的用戶密碼 自動加密 而後去數據庫中對比當前用戶的密碼是否一致 is_right = request.user.check_password(old_password) if is_right: print(is_right) # 修改密碼 request.user.set_password(new_password) request.user.save() # 修改密碼的時候 必定要save保存 不然沒法生效 return render(request,'set_password.html')
註銷用戶: @login_required(/login/url='xxx'/) def logout(request): # request.session.flush() auth.logout(request) 退出 return HttpResponse("logout") 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_obj = User.objects.filter(username=username) if not user_obj: # 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') # 建立超級用戶 return render(request,'register.html')
如何擴張一張表auth_user表呢?
一對一的關聯(不推薦使用)
from django.contrib.auth.model import User class UserDetail(models.Models): phone = models.CharField(max_length=11) user = models.OnoToOneField(to=User)
面向對象的繼承
from django.contrib.auth.models import User,AbstractUser class UserInfo(AbstractUser): phone = models.CharField(max_length=32) # 須要在配置文件中,指定我再也不使用默認的auth_user表而是使用我本身建立的Userinfo表 AUTH_USER_MODEL = "app名.models裏面對應的模型表名" """ 自定義認證系統默認使用的數據表以後,咱們就能夠像使用默認的auth_user表那樣使用咱們的UserInfo表了。 庫裏面也沒有auth_user表了,原來auth表的操做方法,如今所有用自定義的表都可實現 """
使用的userinfo表須要在settings中配置,告訴Django不在自動創user表了,換成user info表
之後在使用時,建立的用戶表能夠添加除了auth_user自帶的字段外,只須要添加額外的字段名。
如如下BBS項目中models.py中user表的建立
數據庫遷移命令執行後會額外的添加字段:
簡單的說就是,當在settings中把中間件某些相關的功能註釋掉以後,某些功能就會失效,打開又可使用
按照settings源碼結構分析,推導得出相應的結論,基於這結論之上,寫一些自定義的中間件:
分析:
根據不一樣功能寫不一樣的文件,秉承python的編成思想用鴨子類型,面向對象式編程寫成類定義相同的def方法,
而後再繼承一樣的send_all,再建立一個settings把全部單獨的功能添加配置。
新建一個文件:包含三個方法:email、msg、wechat __init__
class Email(object): def __init__(self): pass def send(self,content): print('郵件通知:%s'%content)
msg.py
class Msg(object): def __init__(self): pass def send(self,content): print('短信通知:%s'%content)
wechat.py
class WeChat(object): def __init__(self): pass def send(self,content): print('微信通知:%s'%content)
核心部分的代碼 __init__.py
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) # 對象調方法
settings.py
NOTIFY_LIST = [ 'notify.email.Email', 'notify.msg.Msg', 'notify.wechat.WeChat', # 'notify.qq.QQ', ]
start.py
import notify notify.send_all('國慶放假了,學習使我快樂!')
把QQ的註釋掉執行的的結果以下:
---恢復內容結束---