中介軟體和Auth模組

Formerly0^0發表於2024-03-28

中介軟體

1. Django中介軟體介紹

【1】什麼是Django中介軟體
Django中介軟體是一個輕量級、可重用的元件,用於處理Django請求和響應的過程。

它提供了對請求和響應進行全域性處理的機制,可以在請求達到檢視之前進行預處理或在響應返回給客戶端之前進行後處理。

中介軟體是按照順序依次執行的,每個中介軟體都可以對請求和響應進行修改、補充或處理。

在Django的settings.py配置檔案中,透過MIDDLEWARE設定來定義中介軟體的順序。

【2】作用:
認證和授權:

中介軟體可以在請求到達檢視之前進行使用者認證和許可權驗證,確保只有經過授權的使用者才能訪問敏感資源。
請求和響應處理:

中介軟體可以在請求到達檢視之前對請求進行預處理

例如新增請求頭資訊、檢查請求引數的合法性等操作。
同時,在檢視函式返回響應給客戶端之前,中介軟體還可以對響應進行後處理

例如新增額外的響應頭、包裝響應資料等操作。
異常處理:

中介軟體還可以捕獲檢視函式中可能丟擲的異常,並做相應的處理
例如記錄異常日誌、返回自定義錯誤資訊等。
效能最佳化:

透過中介軟體,可以對請求進行效能監測、快取處理、壓縮響應等操作,提升網站的整體效能。
【二】Django中介軟體是Django的門戶
請求發來的時候需要先經過中介軟體才能到達真正的Django後端
響應返回的時候,最後也需要進過中介軟體返回傳送出去
【三】Django中介軟體原始碼分析
【1】預設的五個中介軟體詳解
(1)SecurityMiddleware
django.middleware.security.SecurityMiddleware:
安全中介軟體負責處理與網站安全相關的任務
例如設定HTTP頭部,防止跨站指令碼攻擊(XSS),點選劫持等。
它可以透過配置自定義安全策略來確保網站的安全性。
(2)SessionMiddleware
django.contrib.sessions.middleware.SessionMiddleware:
會話中介軟體負責處理使用者會話的建立之間儲存和檢索使用者資料。
它基於瀏覽器提供的Cookie或URL傳遞的會話ID進行會話跟蹤,並將會話資料儲存在後端資料庫或快取中,以實現使用者狀態的跨請求保持。
(3)CommonMiddleware
django.middleware.common.CommonMiddleware:
通用中介軟體提供了一些常見而關鍵的HTTP請求處理功能
例如,根據請求的HTTP頭資訊設定語言、時區等。
此外,它還處理靜態檔案的serving,包括收集靜態檔案,為其生成URL,並在開發模式下提供靜態檔案的serving。
(4)CsrfViewMiddleware
django.middleware.csrf.CsrfViewMiddleware:
CSRF(Cross-Site Request Forgery)中介軟體用於防止跨站請求偽造攻擊。
它在每個POST請求中驗證一個CSRF標記,確保請求是透過合法的表單提交得到的,從而保護使用者免受惡意站點的攻擊。
(5)AuthenticationMiddleware
django.contrib.auth.middleware.AuthenticationMiddleware:
認證中介軟體負責處理使用者身份認證相關的任務
例如將認證資訊關聯到請求物件上,為每個請求提供一個user物件,以便在請求處理過程中輕鬆地獲取和使用使用者身份資訊。
(6)MessageMiddleware
django.contrib.messages.middleware.MessageMiddleware:
訊息中介軟體用於在請求處理過程中儲存和傳遞臨時的、一次性的使用者訊息。
它允許在HTTP重定向之間跨請求傳遞訊息,例如成功或錯誤提示,以改善使用者體驗。
(7)XFrameOptionsMiddleware
django.middleware.clickjacking.XFrameOptionsMiddleware:
點選劫持中介軟體用於防止頁面被嵌入到其他網站中,從而提供一定的點選劫持保護。
它透過設定X-Frame-Options HTTP頭部來限制頁面的顯示方式,從而防止惡意網頁透過iframe等方式嵌入當前網頁。

2. 中介軟體方法

2.1 必須掌握的中介軟體方法

(1)process_request:

(1)執行順序

  • 請求來的時候需要經過每一箇中介軟體的 process_request 方法
  • 結果的順序是按照配置檔案中註冊的中介軟體從上往下的順序執行的

(2)沒有定義process_request

  • 如果沒有定義這個方法,就跳過這個中介軟體

(3)定義了返回值

  • 如果在自定義中介軟體中定義了返回值(三板斧),那麼請求將不再繼續執行,而是直接原路返回(校驗失敗不允許訪問)

(4)總結

  • process_request 方法就是用來 做全域性相關的所有限制功能
  • 該方法在每個請求到達檢視之前被呼叫,可以對請求進行預處理。
    • 例如,進行身份驗證、訪問控制或請求日誌記錄等操作。
  • 它接收一個HttpRequest物件作為引數,並且沒有返回值。

示例:

class AuthenticationMiddleware:
    def process_request(self, request):
        # 在這裡進行身份驗證操作
        if not request.user.is_authenticated:
            # 如果使用者未經身份驗證,則返回HttpResponse或重定向到登入頁面
(2)process_response:
  • 響應被返回的時候需要結束每一箇中介軟體裡面的 process_response 方法
    • 該方法有兩個額外的引數
      • request
      • response
  • 該方法必須返回 HttpResponse 物件
    • 預設是response
    • 支援自定義
  • 順序是按照配置檔案中註冊過的中介軟體從下往上依次經過
    • 如果沒有定義,則跳過,校驗下一個
  • 該方法在每個請求結束並且響應返回到客戶端之前被呼叫。
    • 可以在此處對響應進行處理
    • 例如新增額外的頭資訊、修改響應內容等。
  • 它接收一個HttpRequest物件和HttpResponse物件作為引數,並且必須返回一個HttpResponse物件。

示例:

class CustomResponseMiddleware:
    def process_response(self, request, response):
        # 在這裡對響應進行處理
        response['X-Custom-Header'] = 'Custom Value'
        return response

2.2 需要了解的中介軟體方法:

(1)process_view:
  • 路由匹配成功後執行檢視函式之前
  • 會自動執行中介軟體裡面的該方法
  • 順序是按照配置檔案中註冊的中介軟體從上而下的順序執行
  • 該方法在請求到達檢視之前被呼叫,在檢視函式執行前執行。
    • 可以在此處進行一些操作
    • 如修改請求引數或進行記錄等。
  • 它接收一個HttpRequest物件和一個檢視函式作為引數,並且可以返回一個HttpResponse物件或None。

示例:

class LoggingMiddleware:
    def process_view(self, request, view_func, view_args, view_kwargs):
        # 在這裡記錄日誌
        logger.info(f"Request received: {request.path}")
        # 返回None,繼續執行原檢視函式
        return None
(2)process_template_response:
  • 返回的 HttpResponse 物件有 render 屬性的時候才會觸發
  • 順序是按照配置檔案中註冊了的中介軟體從下往上依次經過
  • 該方法在檢視函式返回一個TemplateResponse物件時呼叫。
    • 可以在此處修改模板響應
    • 例如新增全域性的上下文資料或進行額外的渲染操作。
  • 它接收一個HttpRequest物件和一個TemplateResponse物件作為引數,並且必須返回一個TemplateResponse物件。

示例:

class GlobalContextMiddleware:
    def process_template_response(self, request, response):
        # 在這裡新增全域性的上下文資料
        response.context_data['global_data'] = "Global Value"
        return response
(3)process_exception:
  • 當檢視函式中出現異常的情況下觸發
  • 順序是按照配置檔案中註冊了的中介軟體從下往上依次經過
  • 該方法在檢視函式丟擲異常時被呼叫。
    • 可以在此處捕獲異常並進行處理
    • 例如返回一個定製的錯誤頁面或進行日誌記錄等。
  • 它接收一個HttpRequest物件和一個異常物件作為引數,可以返回一個HttpResponse物件來替代原始的異常響應。

示例:

class ErrorHandlerMiddleware:
    def process_exception(self, request, exception):
        # 在這裡處理異常
        if isinstance(exception, CustomException):
            # 如果自定義異常,返回一個定製的錯誤頁面
            return render(request, 'error.html', {'error': str(exception)})
        else:
            # 預設情況,返回一個500伺服器錯誤
            return HttpResponseServerError("Internal Server Error")

3. 自定義中介軟體

3.1 process_request`

【1】路由層
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/',views.index),
]
【2】檢視層
def index(request):
    print("這是檢視函式index")
    return HttpResponse("index 的返回值")
【3】配置檔案
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',
    # 註冊自己的中介軟體(在應用下建立路徑會有提示,但是如果在專案下建立就沒有提示,需要自己根據路徑書寫)
    'app01.mymiddle.my_middle.MyMiddle',
    # 誰先註冊就先執行誰
    'app01.mymiddle.my_middle.MyMiddle2',
]
【4】自定義中介軟體
# 引入父類
from django.utils.deprecation import MiddlewareMixin


class MyMiddle(MiddlewareMixin):
    def process_request(self, request):
        print("這是第一個自定義中介軟體中的 process_request 方法")

class MyMiddle2(MiddlewareMixin):
    def process_request(self, request):
        print("這是第二個自定義中介軟體中的 process_request 方法")

4. 總結

(1)執行順序

  • 請求來的時候需要經過每一箇中介軟體的 process_request 方法
  • 結果的順序是按照配置檔案中註冊的中介軟體從上往下的順序執行的

(2)沒有定義process_request

  • 如果沒有定義這個方法,就跳過這個中介軟體

(3)定義了返回值

  • 如果在自定義中介軟體中定義了返回值(三板斧),那麼請求將不再繼續執行,而是直接原路返回(校驗失敗不允許訪問)

(4)總結

  • process_request 方法就是用來 做全域性相關的所有限制功能

5. pathlib模組

【一】引入
我們在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',
]
這種路徑構造方式,我們就可以透過importlib模組實現
【二】推導過程
以多平臺傳送訊息為例

【1.0】引入
定義一個包
def wechat(content):
    print(f"wechat接收到的通知:>>>{content}")


def QQ(content):
    print(f"QQ接收到的通知:>>>{content}")


def email(content):
    print(f"email接收到的通知:>>>{content}")

啟動檔案中啟動包

from send_message import *


def send_all(content):
    wechat(content=content)
    QQ(content=content)
    email(content=content)


if __name__ == '__main__':
    send_all("這是一條測試資訊")

wechat接收到的通知:>>>這是一條測試資訊
QQ接收到的通知:>>>這是一條測試資訊
email接收到的通知:>>>這是一條測試資訊
【2.0】升級
(1)功能部分
先分別建立不同的訊息功能檔案
在同一個資料夾下
建立三個功能檔案

class WeChat(object):
    def __init__(self):
        # 傳送訊息前的準備工作
        # 比如掉介面/初始化配置等
        pass

    def send(self, content):
        print(f"WeChat 傳送的訊息 :>>>{content}")

class QQ(object):
    def __init__(self):
        # 傳送訊息前的準備工作
        # 比如掉介面/初始化配置等
        pass

    def send(self, content):
        print(f"QQ 傳送的訊息 :>>>{content}")
        
class email(object):
    def __init__(self):
        # 傳送訊息前的準備工作
        # 比如掉介面/初始化配置等
        pass

    def send(self, content):
        print(f"email 傳送的訊息 :>>>{content}")
在上面的檔案內建立初始化檔案

import settings
import importlib


def send_all(content):
    # 拿到每一個包的路徑
    for path_str in settings.MODEL_LIST:
        model_path, class_name = path_str.rsplit('.', maxsplit=1)
        # model_path : model.email
        # class_name : email
        # (1)利用字串匯入模組
        # models : 模組物件
        models = importlib.import_module(model_path)
        # (2)利用反射拿到類名
        cls = getattr(models, class_name)
        # (3)生成類的物件
        obj = cls()
        # (4)利用鴨子型別直接呼叫send傳送訊息
        obj.send(content)


if __name__ == '__main__':
    send_all('1')
(3)呼叫部分
在外部定義一個配置檔案

MODEL_LIST = [
    'model.email.email',
    'model.QQ.QQ',
    'model.WeChat.WeChat',
]
在外部的真正功能檔案
import model

model.send_all('這是測試訊息')
email 傳送的訊息 :>>>這是測試訊息
QQ 傳送的訊息 :>>>這是測試訊息
WeChat 傳送的訊息 :>>>這是測試訊息
(4)小結
遵從Python中的鴨子型別
可以在功能檔案中自定義功能檔案新增或者註釋
在 settings.py 檔案中相關的路徑註釋掉或新增上去即可

6. CSRF跨域請求偽造

【一】csrf跨站請求偽造詳解
CSRF(Cross-Site Request Forgery)跨站請求偽造是一種常見的網路攻擊方式。
攻擊者透過誘導受害者訪問惡意網站或點選惡意連結
將惡意請求傳送到目標網站上
利用受害者在目標網站中已登入的身份來執行某些操作
從而達到攻擊的目的。
舉個例子
假設受害者在一家網銀網站上登入賬戶,然後繼續瀏覽其他網頁。
同時,攻擊者透過電子郵件等方式向受害者傳送了一封包含惡意連結的郵件。
當受害者點選該連結時,潛在的威脅就會變得非常現實。
該連結指向一個由攻擊者操縱的網站,該網站上的惡意程式碼會自動向網銀網站傳送一個請求,請求轉賬到攻擊者的賬戶。
由於受害者在網銀網站中已經登入,所以該請求會被認為是合法的,這樣攻擊者就可以成功地進行轉賬操作。
要保護自己免受CSRF攻擊,網站開發者可以採取以下措施:
使用CSRF令牌:
在使用者的請求中新增隨機生成的令牌,並將該令牌儲存在使用者會話中。
每次提交請求時都會驗證該令牌,以確保請求是合法的。
啟用SameSite屬性:
將Cookie的SameSite屬性設定為Strict或Lax,以限制跨站請求。
這可以在一定程度上緩解CSRF攻擊。
嚴格驗證請求來源:
伺服器端可以驗證請求的來源是否為預期的網站域名
例如檢查HTTP Referer頭部。
使用驗證碼:
在敏感操作(如轉賬、更改密碼等)上使用驗證碼
增加使用者身份驗證的防護。
【二】csrf跨域請求偽造
釣魚網站

搭建一個類似正規網站的頁面
使用者點選網站連結,給某個使用者打錢
打錢的操作確確實實提交給了中國銀行的系統,使用者的錢也確實減少
但是唯一不同的是,賬戶打錢的賬戶不是使用者想要打錢的目標賬戶,變成了其他使用者
內部本質

在釣魚網站的頁面針對對方賬戶,只給使用者提供一個沒有name屬性的普通input框
然後在內部隱藏一個已經寫好帶有name屬性的input框
如何避免上面的問題

csrf跨域請求偽造校驗
網站在給使用者返回一個具有提交資料功能的頁面的時候會給這個頁面加一個唯一標識
當這個頁面後端傳送post請求的時候,我們後端會先校驗唯一標識
如果成功則正常執行
如果唯一標識不符合則拒絕連線(403 forbidden)
【三】csrf校驗
【介紹】
csrf校驗是一種用於防止跨站請求偽造(Cross-Site Request Forgery)攻擊的安全措施。
form表單中進行csrf校驗:
新增CSRF Token欄位:
在form表單中新增一個隱藏欄位,用於儲存CSRF Token的值。
後端伺服器在渲染表單時生成一個CSRF Token,並將其儲存在會話中或者以其他方式關聯到當前使用者。
當使用者提交表單時,前端將CSRF Token的值包含在請求中。
後端在驗證表單資料時,檢查請求中的CSRF Token是否與儲存的Token匹配,如果不匹配,則拒絕請求。
設定Cookie:
後端伺服器在渲染表單時,在客戶端設定一個包含隨機生成的CSRF Token的Cookie。
當使用者提交表單時,表單資料會被一同傳送到伺服器,並自動包含該Cookie。
後端在驗證表單資料時,檢查請求中的CSRF Token是否與Cookie中的值匹配,如果不匹配,則拒絕請求。
雙重Cookie校驗:
後端伺服器在渲染表單時,在Cookie中設定一個隨機生成的CSRF Token,並將其儲存在會話中或以其他方式關聯到當前使用者。
當使用者提交表單時,表單資料會被一同傳送到伺服器,請求頭或請求引數中攜帶一個包含CSRF Token的自定義欄位。
後端在驗證表單資料時,同時檢查請求中的CSRF Token和Cookie中的值是否匹配,如果不匹配,則拒絕請求。
【1】form表單如何校驗
在form表單上面加上csrf_token
{% csrf_token %}
<form action="" method="post">
    <p>username:<input type="text" name="username"></p>
    <p>transfer_user<input type="password" name="password"></p>
    <p>money<input type="text" name="money"></p>
    <input type="submit">
</form>
在頁面標籤中會自動出現一個標籤
<input type="hidden" name="csrfmiddlewaretoken" value="zQaNPZsy1tVmLdqC7GIDOOOfR7yT9YfO58lJ5yrjZfTw2edZTrVYUllOVMnkwXKe">
【2】ajax如何校驗
方式一
利用標籤查詢獲取頁面上的隨機字串
鍵必須叫 csrfmiddlewaretoken
<button id="b1">ajax請求提交</button>

<script>
    $("#b1").click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // (1) 利用標籤查詢獲取頁面上的隨機字串
            data: {
              "username": "dream",
              "csrfmiddlewaretoken":$("input[name='csrfmiddlewaretoken']").val()
            },
            success: function () {

            }
        })
    })
</script>
方式二
利用模板語法進行快捷引入
<button id="b1">ajax請求提交</button>

<script>
    $("#b1").click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // (2) 利用模板語法提供的快捷書寫
            data: {"username": "dream", "csrfmiddlewaretoken": "{{ csrf_token }}"},
            success: function () {

            }
        })
    })
</script>
方式三
定義一個js檔案並引入
匯入該配置檔案之前,需要先匯入jQuery,因為這個配置檔案內的內容是基於jQuery來實現的
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);
    }
  }
});

<button id="b1">ajax請求提交</button>

<script>
    $("#b1").click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // (3) 定義外部js檔案並引入到本地
            data: {"username": "dream"},
            success: function () {

            }
        })
    })
</script>

7. CSRF驗證裝飾器

###########  CSRF相關的裝飾器驗證
from django.views.decorators.csrf import csrf_protect, csrf_exempt


# csrf_protect:給檢視函式新增CSRF驗證保護
# csrf_exempt:取消檢視的csrf驗證保護
# @csrf_protect
# @csrf_exempt
# def login(request):
#     if request.method == "POST":
#         return HttpResponse("post")
#     return render(request, "login.html", locals())

###CBV新增csrf驗證裝飾器

# csrf_protect:給檢視函式新增CSRF驗證保護
# csrf_exempt:取消檢視的csrf驗證保護
# @method_decorator(csrf_exempt, name="post") # 取消驗證保護器在類檢視上面無效
# class Login(View):
#     # @method_decorator(csrf_exempt) # 放在類中重寫的 dispatch方法生效
#     def dispatch(self, request, *args, **kwargs):
#         return super().dispatch(request, *args, **kwargs)
#
#     def get(self, request, *args, **kwargs):
#         return render(request, "login.html", locals())
#
#     # @method_decorator(csrf_exempt) # 取消驗證保護器在類中的檢視函式上面無效
#     def post(self, request, *args, **kwargs):
#         return HttpResponse("post")

# @method_decorator(csrf_protect, name="post")  # 增加驗證保護器在類檢視上面有效
class Login(View):
    @method_decorator(csrf_protect) # 放在類中重寫的 dispatch方法生效
    def dispatch(self, request, *args, **kwargs):
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        return render(request, "login.html", locals())

    # @method_decorator(csrf_protect) # 增加驗證保護器在類中的檢視函式上面有效
    def post(self, request, *args, **kwargs):
        return HttpResponse("post")

8. Auth模組

  • 命令列建立超級管理員
python3.10 manage.py createsuperuser
  • 程式碼建立
from django.contrib import auth
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required


def register(request):
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")
        confirm_password = request.POST.get("confirm_password")
        # 校驗兩次密碼是否一致
        if password != confirm_password:
            return HttpResponse("兩次密碼不一致")
        # 使用者註冊
        # 如果走自己的模型表語句是不成立的,建立的密碼是明文
        # obj = User.objects.create(username=username, password=password)
        # 建立普通使用者
        obj = User.objects.create_user(username=username, password=password)
        # 建立超級管理員
        # obj = User.objects.create_superuser(username=username, password=password)
        print(obj)
        return redirect("login")
    return render(request, "register.html", locals())


def login(request):
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")
        # 直接呼叫原始的查詢語句是不成立的,因為密碼是秘文
        # User.object.filter(username=username)
        user_obj = auth.authenticate(username=username, password=password)
        # print(user_obj)
        
        # 儲存使用者狀態,auth.login(request, user_obj)
        if not user_obj:
            return HttpResponse("密碼不正確")

        auth.login(request, user_obj)
    return render(request, "login.html", locals())


def home(request):
    # 確保當前使用者是已經登陸過後的使用者才能訪問
    # print(request.user)
    # request.user獲取到的是上面儲存使用者狀態的使用者物件
    # 正常登陸獲取的是上面儲存的user_obj物件
    # AnonymousUser匿名使用者

    # 第二種獲取使用者物件的登陸狀態 request.user.is_authenticated
    # 返回的是布林值,登陸過是true,沒有登陸過是false
    is_user_login = request.user.is_authenticated
    print(is_user_login)
    if is_user_login:
        return HttpResponse("home")


# 第一種方法
# auth有寫好的登陸認證裝飾器
# @login_required(login_url='login') # 區域性的登陸認證裝飾器
# def index(request):
#     return HttpResponse("index")


# 第二種全域性settings配置登陸路由
@login_required
def index(request):
    return HttpResponse("index")


# 登出
@login_required
def logout(request):
    # auth.logout(request) auth自帶的登出使用者登陸狀態
    auth.logout(request)
    return redirect("login")


@login_required
def change_info(request):
    if request.method == "POST":
        username = request.POST.get("username")
        password = request.POST.get("password")
        confirm_password = request.POST.get("confirm_password")
        new_password = request.POST.get("new_password")
        # 校驗兩次密碼是否一致
        if password != confirm_password:
            return HttpResponse("兩次密碼不一致")
        # 校驗原來的,密碼是否一致
        if not request.user.check_password(password):
            return HttpResponse("密碼不正確!")
        # 修改密碼
        request.user.set_password(new_password)
        # 修改完密碼需要sava儲存
        request.user.save()
        return redirect("login")
    return render(request, "change_info.html", locals())

9. 基於原來的Auth表擴充套件新欄位

【1】方式一
from django.db import models
from django.contrib.auth.models import User, AbstractUser


# Create your models here.
# 擴充套件 auth_user 表
# 第一種方式 : 一對一關係(不推薦)
class UserDetail(models.Model):
    phone = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE)
【2】方式二
from django.db import models
from django.contrib.auth.models import User, AbstractUser

# 第二種方式 : 物件導向的繼承
class UserInfo(AbstractUser):
    '''
    如果繼承了AbstractUser
    那麼在執行資料庫遷移命令的時候,auth_user表就不會被建立
    而 UserInfo 會在 auth_user表 的基礎上新增自定義擴充套件的欄位

    優點:
        直接透過自己定義的錶快速完成操作及擴充套件

    前提
        (1)在執行之前沒有執行過資料庫遷移命令
            auth_user 表沒有被建立
            如果當前庫已經被建立,則需要更換新的庫
        (2)繼承的表裡面不要覆蓋 AbstractUser 裡面的欄位名
            表裡面有的欄位不要動,只擴充套件額外的欄位即可
        (3)需要再配置檔案中宣告Django要使用 UserInfo 替代 auth_user
            AUTH_USER_MODEL = 'app01.UserInfo'  ---'應用名.表名'
    '''
    phone = models.CharField(max_length=32)
需要再配置檔案中宣告 Django 要使用 UserInfo 替代 auth_user
 AUTH_USER_MODEL = 'app01.UserInfo'  ---'應用名.表名'
如果自己寫表代替了 auth_user
auth模組功能正常使用,參考的表也由 auth_user 變成了 UserInfo

相關文章