Django中間件-跨站請求僞造-django請求生命週期-Auth模塊-seettings實現可插拔配置(設計思想)

Django中間件

1、什麼是中間件

  django中間件就是相似於django的保安;請求來的時候須要先通過中間件,才能到達django後端(url,views,models,templates),css

響應走的的時候也須要通過中間件才能到達web服務器網關接口處;html

中間件位於web服務端與url路由層之間;是介於request與response處理之間的一道處理過程。前端

2、中間件有什麼用

  若是你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些均可以經過中間件來實現。python

能夠用來作什麼?

  一、網站全局的身份校驗,訪問頻率限制,權限檢驗等;只要涉及到全局校驗的均可以用中間件來實現jquery

  二、Django的中間件是全部的web框架中作得最好的web

Django默認的中間件:(在django項目的settings模塊中,有一個MIDDLEWARE_CLASSES變量,其中的每個元素就是一箇中間件)ajax

django默認的中間件有七個以下圖:

 3、自定義中間件

  中間件能夠定義五個方法;其中主要的是(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和process_response

  當用戶發起請求的時候會依次通過全部的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。

以下圖所示,一個完整的中間件的流程:

二、Djang請求生命週期:

wsgi---中間件---路由---視圖---中間件---wsgi--

  經過完整Django的完整構造圖,可以擴展結合Django每一個知識點,深刻了解每一個知識點所涉及到的內容。

三、須要重點掌握的中間件方法:

  一、.process_request()方法

    規律:

      一、請求來的時候,會通過每一箇中間件裏面的process_request()方法(從上到下的順序)

      二、若是返回的是HttpResponse對象,那麼會直接返回,再也不往下執行了;基於這一特色就能夠作訪問的頻率限制,身份校驗,權限校驗等

  二、process_response()方法

   規律:

    (1)、必須將response形參返回,由於這個形參指代的就是要返回給前端的數據。

    (2)、響應走的時候,會依次通過每個中間件裏面的process_response方法(從下往上)

須要瞭解的方法:

    (1)、process_view() :

            在路由匹配成功執行視圖函數以前 觸發

    (2)、process_exception() :

            當你的視圖函數報錯時  就會自動執行

    (3)、process_template_response() 

            當你返回的HttpResponse對象中必須包含render屬性纔會觸發

 四、自定義的中間件,寫的類必須繼承 MiddlewareMixin

 (1)第一步:導入

from django.utils.deprecation import MiddlewareMixin

 (2)、自定義中間件,新建文件件書寫

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

 (3):在views中定義一個視圖視圖函數(index)

def index(request):

    print("view函數...")
    return HttpResponse("OK")

(4)、在settings.py的MIDDLEWARE裏註冊本身定義的中間件

1.若是你想讓你寫的中間件生效,就必需要先繼承MiddlewareMixin
2.在註冊自定義中間件的時候,必定要確保路徑不要寫錯

(5)查看運行的結果:得出上面的總結規律

請求得出的規律:

返回得出的規律:

第二種狀況,當自定義的中間件種有HttpRsponse時,直接返回:

(6)若是沒有返回response形參,由於這個形參指代的就是要返回給前端的數據

 報錯結果顯示:

 (7)、其餘方法瞭解:

一、process_view

  該方法有四個參數

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,該方法兩個參數:

process_exception(self, request, exception)

 

一個HttpRequest對象

一個exception是視圖函數異常產生的Exception對象。

三、process_template_response(self,request,response)方法:

  該方法對視圖函數返回值有要求,必須是一個含有render方法類的對象,纔會執行此方法

總結:你在書寫中間件的時候 只要形參中有repsonse 你就順手將其返回 這個reponse就是要給前端的消息

 2、CSRF_TOKEN跨站請求僞造

  一、什麼是csrf

  CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲「One Click Attack」或者Session Riding;

簡單的理解:就是攻擊者盜用了你的身份,以你的名義發送惡意的請求,對服務器來講這個請求是徹底合法的;

要完成一次CSRF攻擊,受害者必須依次完成兩個步驟:

  1.登陸受信任網站A,並在本地生成Cookie。
  2.在不登出A的狀況下,訪問危險網站B。

簡單的舉例釣魚網站:開兩個django項目,模擬轉帳的現象

正規的網站:

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 %},會在客戶端生成一對鍵值對

二、用AJAX發送post請求時,如何避免csrf校驗

  (1)、如今頁面上寫{% csrf_token %},利用標籤查找 ,獲取到該input鍵值信息,關鍵字:'csrfmiddlewaretoken'

{'username':'jason','csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}

 

   (2)、直接書寫'{{ csrf_token }}'

{'username':'jason','csrfmiddlewaretoken':'{{ csrf_token }}'}

  (3)、你能夠將該獲取隨機鍵值對的方法,寫到一個js文件中,以後只須要導入該文件便可使用。

添加static到settings.Py中:

 而後在使用的前端html頁面導入:

書寫靜態文件存放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);
    }
  }
});

 3、跨站請求僞造相關的裝飾器

    1.當你網站全局都須要校驗csrf的時候 有幾個不須要校驗該如何處理?
     2.當你網站全局不校驗csrf的時候 有幾個須要校驗又該如何處理?

全站禁用:註釋掉中間件 'django.middleware.csrf.CsrfViewMiddleware',

局部禁用:用裝飾器(在FBV中使用)

 在CBV中使用:

   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認證模塊

  執行數據庫遷移的那兩條命令時,即便咱們沒有建表,django是否是也會建立好多張表?

咱們建立以後去看一下里面的一個叫auth_user表,這個表跟用戶的相關,既然是表,那確定應該有對應的操做改表的方法

 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功能

  如何擴張一張表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自帶的字段外,只須要添加額外的字段名。

新建立的django的user表都會自帶如下一些字段:

如如下BBS項目中models.py中user表的建立

 數據庫遷移命令執行後會額外的添加字段:

 

 基於Django 中間件思想,實現可插拔功能

  簡單的說就是,當在settings中把中間件某些相關的功能註釋掉以後,某些功能就會失效,打開又可使用

按照settings源碼結構分析,推導得出相應的結論,基於這結論之上,寫一些自定義的中間件:

舉例開發一個實現集體通信發信息,如用短信、微信、QQ發

分析:

  根據不一樣功能寫不一樣的文件,秉承python的編成思想用鴨子類型,面向對象式編程寫成類定義相同的def方法,

而後再繼承一樣的send_all,再建立一個settings把全部單獨的功能添加配置。

新建一個文件:包含三個方法:email、msg、wechat __init__

email.py

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的註釋掉執行的的結果以下:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

 

 

 

 

 

  

 

 

 

 

---恢復內容結束---

相關文章
相關標籤/搜索