基於session實現登陸javascript
def login(request): if request.method == 'POST': username = request.POST.get('username') pwd = request.POST.get('password') if username=='jason' and pwd=='123': request.session['name'] = 'jason' # 這一步是添加session return redirect('/home/') return render(request,'login.html')
若是說後面咱們遇到了多個頁面這些操做,都要實現校驗,那麼就得用到了裝飾器,給類添加裝飾器怎麼添加?css
裝飾器html
from functools import wraps def login_auth(func): @wraps(func) def inner(request,*args,**kwargs): if request.session.get('name'): return func(request,*args,**kwargs) return redirect('/login') return inner
類添加裝飾器前端
# 給CBV添加裝飾器的方法 ''' 1.第一種直接再類上面,必須指定name參數 2.直接再類裏面的函數添加 3.重寫dispatch方法 4.前端action裏面修改路由 ''' # @method_decorator(login_auth,name='get') # 第一種 class Myhome(View): @method_decorator(login_auth) # 第三種 只要是總路由裏面的均可以被裝飾 def dispatch(self, request, *args, **kwargs): super().dispatch(request,*args,**kwargs) # @method_decorator(login_auth) # 第二種 def get(self,request): return HttpResponse('get') def post(self,request): return HttpResponse('post')
官方的說法:中間件是一個用來處理Django的請求和響應的框架級別的鉤子。它是一個輕量、低級別的插件系統,用於在全局範圍內改變Django的輸入和輸出。每一箇中間件組件都負責作一些特定的功能。java
可是因爲其影響的是全局,因此須要謹慎使用,使用不當會影響性能。jquery
說的直白一點中間件是幫助咱們在視圖函數執行以前和執行以後均可以作一些額外的操做,它本質上就是一個自定義類,類中定義了幾個方法,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配置項是一個列表(列表是有序的),列表中是一個個字符串,這些字符串實際上是一個個類,也就是一個個中間件。django默認有七個中間件,可是django暴露給用戶能夠自定義中間件而且裏面能夠寫五種方法bootstrap
請求來了以後會依次通過每個門戶,纔會到urls.py後端
查看具體方法
class SecurityMiddleware(MiddlewareMixin): def __init__(self, get_response=None): self.sts_seconds = settings.SECURE_HSTS_SECONDS self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS self.sts_preload = settings.SECURE_HSTS_PRELOAD self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF self.xss_filter = settings.SECURE_BROWSER_XSS_FILTER self.redirect = settings.SECURE_SSL_REDIRECT self.redirect_host = settings.SECURE_SSL_HOST self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT] self.get_response = get_response def process_request(self, request): path = request.path.lstrip("/") if (self.redirect and not request.is_secure() and not any(pattern.search(path) for pattern in self.redirect_exempt)): host = self.redirect_host or request.get_host() return HttpResponsePermanentRedirect( "https://%s%s" % (host, request.get_full_path()) ) def process_response(self, request, response): if (self.sts_seconds and request.is_secure() and 'strict-transport-security' not in response): sts_header = "max-age=%s" % self.sts_seconds if self.sts_include_subdomains: sts_header = sts_header + "; includeSubDomains" if self.sts_preload: sts_header = sts_header + "; preload" response["strict-transport-security"] = sts_header if self.content_type_nosniff and 'x-content-type-options' not in response: response["x-content-type-options"] = "nosniff" if self.xss_filter and 'x-xss-protection' not in response: response["x-xss-protection"] = "1; mode=block" return response
ps:
1.請求來的時候會依次執行每個中間件裏面的process_request方法,若是沒有直接經過
2.響應走的時候會依次執行每個中間件裏面的process_response方法,若是沒有依次經過(後面在補充,有點小坑)
中間件能夠定義五個方法,分別是:(主要的是process_request和process_response)
以上方法的返回值能夠是None或一個HttpResponse對象,若是是None,則繼續按照django定義的規則向後繼續執行,若是是HttpResponse對象,則直接將該對象返回給用戶。
新建一個任意名字的文件夾,裏面新建一個任意名字的文件夾
中間件本質就是含有5個能夠修改的內置方法的類,因此自定義的時候須要作的就是先繼承一個Django提供的中間件混合父類--MiddlewareMixin
而後記得去settings中配置中間件
請求來了以後會依次往下走
請求走的時候也是從下往上依次執行,可是咱們要知道的是,django中說你在當前這個中間件中的process_request中返回了HttpResponse的話,會直接走這個中間件的response方法,直接按照這個中間件的順序從下往上返回信息。
具體信息查看
注意:若是你的自定義process_response不返回一個return response的話,會直接報錯
報錯信息是
在自定義的中間件中process_response返回了response
process_request有一個參數,就是request,這個request和視圖函數中的request是同樣的(在交給Django後面的路由以前,對這個request對象能夠進行一系列的操做)。
因爲request對象是同樣的,因此咱們能夠對request對象進行一系列的操做,包括request.變量名=變量值,這樣的操做,咱們能夠在後續的視圖函數中經過相同的方式便可獲取到咱們在中間件中設置的值。
它的返回值能夠是None也能夠是HttpResponse對象。返回值是None的話,按正常流程繼續走,交給下一個中間件處理,若是是HttpResponse對象,Django將不執行視圖函數,而將相應對象返回給瀏覽器。
咱們來看看多箇中間件時,Django是如何執行其中的process_request方法的。
class Mymiddlewera(MiddlewareMixin): def process_request(self,request): print('我是app02中mymiddlewera中第一個自定義的中間件方法請求方法') class Mymiddlewera2(MiddlewareMixin): def process_request(self,request): print('我是app02中mymiddlewera中第二個自定義的中間件方法請求方法')
settings中配置
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', 'app02.mymiddlewera.middle.Mymiddlewera', # 自定義中間件1 'app02.mymiddlewera.middle.Mymiddlewera2' # 自定義中間件2 ]
經過終端打印,咱們發現中間件是自上而下依次執行
而後咱們調換一下位置
在打印一下兩個自定義中間件中process_request方法中的request參數,會發現它們是同一個對象。
總結:
由此總結一下:
多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最早執行。
定義process_response方法時,必須給方法傳入兩個形參,request和response。request就是上述例子中同樣的對象,response是視圖函數返回的HttpResponse對象(也就是說這是Django後臺處理完以後給出一個的一個具體的視圖)。該方法的返回值(必需要有返回值)也必須是HttpResponse對象。若是不返回response而返回其餘對象,則瀏覽器不會拿到Django後臺給他的視圖
經過咱們上面的代碼和圖片演示,咱們直接進行總結
總結:
process_response方法是在視圖函數以後執行的,而且順序是從下往上依次執行。
多箇中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一箇中間件的process_request方法最後一個執行,它的process_response方法是最早執行。
process_view(self, request, view_func, view_args, view_kwargs)
該方法有四個參數
request是HttpRequest對象。
view_func是Django即將使用的視圖函數。 (它是實際的函數對象,而不是函數的名稱做爲字符串。)
view_args是將傳遞給視圖的位置參數的列表.
view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。
Django會在調用視圖函數以前調用process_view方法。
它應該返回None或一個HttpResponse對象。 若是返回None,Django將繼續處理這個請求,執行任何其餘中間件的process_view方法,而後在執行相應的視圖。 若是它返回一個HttpResponse對象,那麼將不會執行Django的視圖函數,而是直接在中間件中掉頭,倒敘執行一個個process_response方法,最後返回給瀏覽器
class Mymiddlewera(MiddlewareMixin): def process_request(self,request): print('我是app02中mymiddlewera中第一個自定義的中間件方法請求方法') print('111',request) # return HttpResponse('好想好好的睡個一晚上直到天亮') def process_response(self,requset,response): print('我是app02中mymiddlewera中第一個自定義的中間件方法響應方法') return response # 必須將response接收到的數據返回,否則報錯 def process_view(self,request,view_func,view_args,view_kwargs): print('111,你說啥就是啥吧') print(view_func) print(view_args) print(view_kwargs)
終端打印結果
process_view方法是在Django路由系統以後,視圖系統以前執行的,執行順序按照MIDDLEWARE中的註冊順序從前到後順序執行的
process_exception(self, request, exception)
該方法兩個參數:
一個HttpRequest對象
一個exception是視圖函數異常產生的Exception對象。
這個方法只有在視圖函數中出現異常了才執行,它返回的值能夠是一個None也能夠是一個HttpResponse對象。若是是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給瀏覽器,不然將默認處理異常。若是返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件註冊順序的倒序執行。
class Mymiddlewera(MiddlewareMixin): def process_request(self,request): print('我是app02中mymiddlewera中第一個自定義的中間件方法請求方法') print('111',request) # return HttpResponse('好想好好的睡個一晚上直到天亮') def process_response(self,requset,response): print('我是app02中mymiddlewera中第一個自定義的中間件方法響應方法') return response # 必須將response接收到的數據返回,否則報錯 def process_view(self,request,view_func,view_args,view_kwargs): print('111,你說啥就是啥吧') print(view_func) print(view_args) print(view_kwargs) def process_exception(self,request,exception): print('222,你可真煩人啊!') print(exception)
process_template_response(self, request, response)
它的參數,一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)。
process_template_response是在視圖函數執行完成後當即執行,可是它有一個前提條件,那就是視圖函數返回的對象有一個render()方法(或者代表該對象是一個TemplateResponse對象或等價方法)。
class Mymiddlewera(MiddlewareMixin): def process_request(self,request): print('我是app02中mymiddlewera中第一個自定義的中間件方法請求方法') print('111',request) # return HttpResponse('好想好好的睡個一晚上直到天亮') def process_response(self,requset,response): print('我是app02中mymiddlewera中第一個自定義的中間件方法響應方法') return response # 必須將response接收到的數據返回,否則報錯 def process_view(self,request,view_func,view_args,view_kwargs): print('111,你說啥就是啥吧') print(view_func) print(view_args) print(view_kwargs) def process_exception(self,request,exception): print('222,你可真煩人啊!') print(exception) def process_template_response(self,request,response): print('我是app02中的mymiddlewera中第一個自定義的render方法') return response
views.py
def index(request): print('我是index,哈哈哈哈') # print(request.method) def render(): print('567890') # return 123 return HttpResponse('haodezhidaole ') obj = HttpResponse('ok') # print(1111) obj.render=render # 這一步實際上觸發了process return obj
視圖函數執行完以後,當即執行了中間件的process_template_response方法,順序是倒序,先執行MD1的,在執行MD2的,接着執行了視圖函數返回的HttpResponse對象的render方法,返回了一個新的HttpResponse對象,接着執行中間件的process_response方法。
總結:
process_request:請求來的時候從下往上依次執行每個中間件裏面的process_request
process_response:響應走的時候會從下往上依次執行執行每個中間件的process_response方法
process_view:路由匹配成功,執行視圖函數以前自動觸發(順序是從上往下依次執行),view_func是執行視圖函數的名字
process_exception:當視圖函數出現報錯,會自動觸發,順序是依次往下往上執行
process_template_response:當你返回對象的時候有一個render()方法的時候會觸發,執行順序從下往上依次執行
***************
django中漸漸暗可以幫我實現 網站全局的身份驗證,黑名單,白名單,訪問頻率限制,反爬相關等等
》》》:django用來幫你作全局相關的功能校驗
RBAC:基於角色的權限管理(不一樣的客戶能夠給出不一樣的訪問權限)
因此後面只要是涉及到了全局相關,咱們就能夠直接再中間件這裏進行操做,好比說去數據庫中進行校驗這個用戶權限,而後只對他開放特定的功能塊兒!
釣魚網站就是建一個和正常的網站如出一轍的網站,而後用戶在輸入的時候調的也是正常網站的接口去處理,因此用戶的錢會扣掉,可是並無轉給指定的人,其實就是建了一個和正常網站如出一轍的東西,而後偷偷的在轉給目標用戶那裏,偷偷的將input框當前的name去掉,而後用了一個hidden隱藏起來,在隱藏起來的input框中給一個默認的value,具體示例以下
模擬釣魚網站示例
正常網站 views.py
def transfer(request): if request.method == 'POST': username = request.POST.get('name') money = request.POST.get('money') others = request.POST.get('others') print('%s 給 %s 轉了 %s塊錢'%(username,others,money)) return render(request,'transfer.html')
正常網站transfer.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> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> </head> <body> <h1>正經網站</h1> <form action="" method="post"> <p>name:<input type="text" name="name"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text" name="others"></p> <input type="submit"> </form> </body> </html>
釣魚網站 transfer.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> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> </head> <body> <h1>釣魚網站</h1> <form action="http://127.0.0.1:8000/transfer" method="post"> <p>name:<input type="text" name="name"></p> <p>money:<input type="text" name="money"></p> <p>others: <input type="text" > <input type="hidden" name="others" value="mcc" style="display: none" > </p> <input type="submit"> </form> </body> </html>
這樣就是最開始的網絡詐騙ヽ(*。>Д<)o゜ヽ(*。>Д<)o゜
那咱們學習了之後,如何保證咱們的網站的安全呢?🤭🤭🤭,這個時候咱們就想到了咱們剛開始學習讓註冊掉的那個
django.middleware.csrf.CsrfViewMiddleware
中間件,他就是用來作校驗的,他會每次都會在頁面動態刷新生成一個value,而後每次用key值來比對這個value,經過了纔會作下一步操做,否則的話就會直接拒絕這個請求,寫法也很簡單
咱們直接在form表單中寫{% csrf token%},這個時候,django的中間件就會自動的幫咱們去校驗,他會每次都動態的生成一個驗證碼,只要頁面刷新驗證碼就會不一樣,具體以下
<!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> <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css"> <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script> </head> <body> <h1>正經網站</h1> <form action="" method="post"> {% csrf_token %} <p>name:<input type="text" name="name"></p> <p>money:<input type="text" name="money"></p> <p>others:<input type="text" name="others"></p> <input type="submit"> </form> </body> </html>
這樣的話,釣魚網站怎麼也訪問不了
讓你再騙人,😕😕😕
具體作法看圖
每次這個value都會刷新,這樣的話釣魚網站怎麼也訪問不了
這個中間件這麼神奇,可是咱們中間也有一些函數不想校驗,具體作法以下
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt def index(request): pass
@csrf_protect def login(request): pass
from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt,csrf_protect
三種方法均可以用,和正常的CBV裝飾器方法同樣
給CBV取消單個校驗(csrf-exempt)
不能給單獨的視圖函數添加,只有dispatch能夠,是由於給全局加的
直接給類加的,就要指定name='dispatch'
總結:其實都是給dispatch加,類就是一個路由,裏面不能夠單獨寫, --所有都要加到全局才能夠
@method_decorator(csrf_exempt,name='dispatch') # 第一種 class Csrf_Token(View): @method_decorator(csrf_exempt) # 第二種 def dispatch(self,request,*args,**kwargs): res = super().dispatch(request,*args,**kwargs) return res @method_decorator(csrf_exempt) # 這裏這麼寫不行!!! def get(self,request): pass def post(self,request): pass
Auth模塊是Django自帶的用戶認證模塊:
咱們在開發一個網站的時候,無可避免的須要設計實現網站的用戶系統。此時咱們須要實現包括用戶註冊、用戶登陸、用戶認證、註銷、修改密碼等功能,這還真是個麻煩的事情呢。
Django做爲一個完美主義者的終極框架,固然也會想到用戶的這些痛點。它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據。
from django.contrib import auth
提供了用戶認證功能,即驗證用戶名以及密碼是否正確,通常須要username 、password兩個關鍵字參數。
若是認證成功(用戶名和密碼正確有效),便會返回一個 User 對象。
authenticate()會在該 User 對象上設置一個屬性來標識後端已經認證了該用戶,且該信息在後續的登陸過程當中是須要的。
用法:
user = authenticate(username='usernamer',password='password')
該函數接受一個HttpRequest對象,以及一個通過認證的User對象。
該函數實現一個用戶登陸的功能。它本質上會在後端爲該用戶生成相關session數據。
用法:
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid login' error message. ...
該函數接受一個HttpRequest對象,無返回值。
當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。
用法:
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
用來判斷當前請求是否經過了認證。
def my_view(request): if not request.user.is_authenticated(): return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))
auth 給咱們提供的一個裝飾器工具,用來快捷的給某個視圖添加登陸校驗。
用法:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' 並傳遞當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。
若是須要自定義登陸的URL,則須要在settings.py文件中經過LOGIN_URL進行修改。
示例:
LOGIN_URL = '/login/' # 這裏配置成你項目登陸頁面的路由
auth 提供的一個建立新用戶的方法,須要提供必要參數(username、password)等。
用法:
from django.contrib.auth.models import User user = User.objects.create_user(username='用戶名',password='密碼',email='郵箱',...)
auth 提供的一個建立新的超級用戶的方法,須要提供必要參數(username、password)等。
用法:
from django.contrib.auth.models import User user = User.objects.create_superuser(username='用戶名',password='密碼',email='郵箱',...)
auth 提供的一個檢查密碼是否正確的方法,須要提供當前請求用戶的密碼。
密碼正確返回True,不然返回False。
用法:
ok = user.check_password('密碼')
auth 提供的一個修改密碼的方法,接收 要設置的新密碼 做爲參數。
注意:設置完必定要調用用戶對象的save方法!!!
用法:
user.set_password(password='') user.save()
User對象屬性:username, password
is_staff : 用戶是否擁有網站的管理權限.
is_active : 是否容許用戶登陸, 設置爲 False,能夠在不刪除用戶的前提下禁止用戶登陸。
這內置的認證系統這麼好用,可是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表了。