Django-內置的auth模塊

1、auth認證

咱們在開發一個網站的時候,無可避免的須要設計實現網站的用戶系統。此時咱們須要實現包括用戶註冊、用戶登陸、用戶認證、註銷、修改密碼等功能,這還真是個麻煩的事情呢。javascript

  Django做爲一個完美主義者的終極框架,固然也會想到用戶的這些痛點。它內置了強大的用戶認證系統--auth,它默認使用 auth_user 表來存儲用戶數據,使用auth模塊來進行用戶認證,那麼須要使用人家django自帶的auth_user表來存儲用戶的信息數據。html

  模塊導入:前端

from django.contrib import auth

  那麼有人就有疑問 了,這個auth_user表並非咱們本身在models.py文件中建立的啊,這經過代碼怎麼操做啊?java

  其中一個往auth_user表裏面添加數據的命令,能夠先多添加幾個用戶,方便後面操做:python

python manage.py createsuperuser  #要經過這個指令來建立用戶,由於這個指令會將你的密碼加密。

    

    而後表中就有數據了:這個表裏面的數據如今先關注username和password字段就能夠了,其餘的字段可爲空。數據庫

    

user表具備一下字段:

內置的User模型擁有如下的字段:
username: 用戶名。150個字符之內。能夠包含數字和英文字符,以及_、@、+、.和-字符。不能爲空,且必須惟一!
first_name:歪果仁的first_name,在30個字符之內。能夠爲空。
last_name:歪果仁的last_name,在150個字符之內。能夠爲空。
email:郵箱。能夠爲空。
password:密碼。通過哈希事後的密碼。
#groups:分組。一個用戶能夠屬於多個分組,一個分組能夠擁有多個用戶。groups這個字段是跟Group的一個多對多的關係。
#user_permissions:權限。一個用戶能夠擁有多個權限,一個權限能夠被多個用戶全部用。和Permission屬於一種多對多的關係。
is_staff:是否能夠進入到admin的站點。表明是不是員工。這個字段若是不使用admin的話,能夠自行忽略,不影響使用
is_active:是不是可用的。對於一些想要刪除帳號的數據,咱們設置這個值爲False就能夠了,而不是真正的從數據庫中刪除。
is_superuser:是不是超級管理員。若是是超級管理員,那麼擁有整個網站的全部權限。
last_login:上次登陸的時間。
date_joined:帳號建立的時間。

2、認證流程

一、用戶註冊

auth 提供的兩種建立新用戶的方法,須要提供必要參數(username、password)等。django

普通用戶:create_user()後端

超級管理員:create_superuser()session

from django.contrib.auth.models import User
user = User.objects.create_user(username='用戶名',password='密碼',email='郵箱',...)

user_obj = User.objects.create_superuser(username='用戶名',password='密碼',email='郵箱',...)

註冊代碼:app

# 註冊
def register(request):

    if request.method=="GET":
        return render(request,"register.html")
    else:
        username = request.POST.get("username")
        password = request.POST.get("password")
        # 判斷用戶名是否存在
        if User.objects.filter(username=username).exists():
            return HttpResponse("用戶名已經存在")
        # 註冊用戶
        User.objects.create_user(username=username,password=password)
        return redirect("user_login")
註冊

二、用戶登陸

提供了用戶認證功能,即驗證用戶名以及密碼是否正確,通常須要username 、password兩個關鍵字參數,由於你仔細看看auth_user表的話,你會發現用戶名和密碼的字段名稱就是username和password。

若是認證成功(用戶名和密碼正確有效,就是去auth_user表中查詢一下是否存在這條記錄),便會返回一個 User 對象,查詢認證失敗返回None。

authenticate()會在該 User 對象上設置一個屬性來標識後端已經認證了該用戶,且該信息在後續的登陸過程當中是須要的。

user = auth.authenticate(username='theuser',password='thepassword')

登陸代碼:

# 登陸
def user_login(request):
    if request.method=="GET":
        return render(request,"user_login.html")
    else:
        # 登陸驗證
        username = request.POST.get("username")
        password = request.POST.get("password")
        # 判斷用戶名密碼是否正確,輸入正確返回含用戶信息的對象,輸入錯誤返回None
        user_obj = auth.authenticate(username=username,password=password)
        if user_obj:
            # 登陸成功設置session(幹了和session同樣的事情),request,user=user_obj(把查詢到的user_obj的model對象給了request.user)
            auth.login(request,user_obj)
            return redirect("home")
        else:
            # 登陸失敗重定向登陸界面
            return redirect("user_login")
登陸

三、首頁

login(HttpRequest, user)

該函數接受一個HttpRequest對象,以及一個通過認證的User對象。

該函數實現一個用戶登陸的功能。它本質上會在後端爲該用戶生成相關session數據,保持會話用。

# 首頁
def home(request):
    print(request.user) # 是當前登錄的user對象,而且能夠在模板語言裏面直接使用{{ request.user.username }},萬能的句點號
    print(request.user.id) # user表中的用戶id
    print(request.user.username) # 登陸用戶的名稱
    print(request.user.is_active) # 登陸狀態
    # 判斷當前是不是用戶登陸的
    if request.user.is_authenticated():
            return render(request,"home.html")
    else:
        return redirect("user_login")
首頁

四、註銷

logout(request) 

該函數接受一個HttpRequest對象,無返回值。

當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯。

其實內部就是執行了request.session.flush()

# 註銷
def logout(request):
    # 清空session
    auth.logout(request)
    return redirect("user_login")
刪除session

五、修改密碼

check_password(raw_password)

auth 提供的一個檢查密碼是否正確的方法,須要提供當前請求用戶的密碼。

密碼正確返回True,不然返回False。

ok = user_obj.check_password('密碼')

或者直接針對當前請求的user對象校驗原密碼是否正確:

ok = request.user.check_password(raw_password='原密碼')

set_password(raw_password)

auth 提供的一個修改密碼的方法,接收 要設置的新密碼 做爲參數。

注意:設置完必定要調用用戶對象的save方法!!!

user_obj.set_password('新密碼')  #user_obj其實就是request.user
user_obj.save()                 #request.user.save()

修改代碼:

# 修改密碼
def set_password(request):
    if request.method=="GET":
        return render(request,"set_password.html")
    else:

        old_password = request.POST.get("old_password")
        new_password = request.POST.get("new_password")
        r_new_password = request.POST.get("r_new_password")
        print(new_password)
        print(r_new_password)
        # 判斷用戶輸入的舊密碼是否與數據庫中的一致
        if request.user.check_password(old_password):
            if new_password != r_new_password:
                return HttpResponse("兩次輸入的密碼不一致")
            else:
                # 修改密碼
                request.user.set_password(new_password)
                request.user.save()
                return redirect("user_login")
        else:
            return HttpResponse("舊密碼輸入有誤!!!")
密碼修改

用戶對象的屬性

user_obj可以拿到認證所用用戶表的數據屬性,好比username, password等。

其餘經常使用屬性含義以下:

   is_staff : 用戶是否擁有網站的管理權限.

   is_active : 是否容許用戶登陸, 設置爲 False,能夠在不刪除用戶的前提下禁止用戶登陸。

注意:

只要使用login(request, user_obj)以後,request.user就能拿到當前登陸的用戶對象。不然request.user獲得的是一個匿名用戶對象(AnonymousUser Object,是request.user的默認值),這個匿名用戶的狀態在個人視圖函數博客的那個request那一節有介紹,都是空。

詳細原理請查看 AuthenticationMiddleware 中間件源碼。

使用login方法以前,打印user的狀態

def index(request):
    print(request.user) #沒有通過login方法來封裝用戶的信息,那麼這個顯示的是一個匿名用戶
    print(request.user.id) #None
    print(request.user.username) #空的
    print(request.user.is_active) #False
    return render(request,'index.html')

使用login方法以後,打印user的狀態

def index(request):
    print(request.user) #chao,request.user對象自己是全局的,是當前登錄的user對象,而且能夠在模板語言裏面直接使用{{ request.user.username }},萬能的句點號
    print(request.user.id) #1  #經過id、username等能夠判斷用戶是否登錄了,可是通常咱們都用後面要學的is_authenticated()方法來進行判斷。
    print(request.user.username) #chao
    print(request.user.is_active) #True
    return render(request,'index.html')

3、整套認證流程代碼

from django.shortcuts import render,HttpResponse,redirect
from django.contrib.auth.models import User
from django.contrib import auth
# Create your views here.

# 註冊
def register(request):

    if request.method=="GET":
        return render(request,"register.html")
    else:
        username = request.POST.get("username")
        password = request.POST.get("password")
        # 判斷用戶名是否存在
        if User.objects.filter(username=username).exists():
            return HttpResponse("用戶名已經存在")
        # 註冊用戶
        User.objects.create_user(username=username,password=password)
        return redirect("user_login")

# 登陸
def user_login(request):
    if request.method=="GET":
        return render(request,"user_login.html")
    else:
        # 登陸驗證
        username = request.POST.get("username")
        password = request.POST.get("password")
        # 判斷用戶名密碼是否正確,輸入正確返回含用戶信息的對象,輸入錯誤返回None
        user_obj = auth.authenticate(username=username,password=password)
        if user_obj:
            # 登陸成功設置session(幹了和session同樣的事情),request,user=user_obj(把查詢到的user_obj的model對象給了request.user)
            auth.login(request,user_obj)
            return redirect("home")
        else:
            # 登陸失敗重定向登陸界面
            return redirect("user_login")

# 首頁
def home(request):
    print(request.user) # 是當前登錄的user對象,而且能夠在模板語言裏面直接使用{{ request.user.username }},萬能的句點號
    print(request.user.id) # user表中的用戶id
    print(request.user.username) # 登陸用戶的名稱
    print(request.user.is_active) # 登陸狀態
    # 判斷當前是不是用戶登陸的
    if request.user.is_authenticated():
            return render(request,"home.html")
    else:
        return redirect("user_login")

# 註銷
def logout(request):
    # 清空session
    auth.logout(request)
    return redirect("user_login")

# 修改密碼
def set_password(request):
    if request.method=="GET":
        return render(request,"set_password.html")
    else:

        old_password = request.POST.get("old_password")
        new_password = request.POST.get("new_password")
        r_new_password = request.POST.get("r_new_password")
        print(new_password)
        print(r_new_password)
        # 判斷用戶輸入的舊密碼是否與數據庫中的一致
        if request.user.check_password(old_password):
            if new_password != r_new_password:
                return HttpResponse("兩次輸入的密碼不一致")
            else:
                # 修改密碼
                request.user.set_password(new_password)
                request.user.save()
                return redirect("user_login")
        else:
            return HttpResponse("舊密碼輸入有誤!!!")
視圖函數
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>註冊用戶:</h1>
<form action="{% url "register" %}" method="post">
    {% csrf_token %}
    用戶名:<input type="text" name="username">
    密碼:<input type="password" name="password">
    <input type="submit" value="註冊">

</form>
</body>
</html>
register
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用戶登陸:</h1>
<form action="{% url "user_login" %}" method="post">
    {% csrf_token %}
    用戶名:<input type="text" name="username">
    密碼:<input type="password" name="password">
    <input type="submit" value="登陸">

</form>
</body>
</html>
user_login
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>當前登陸用戶:{{ request.user.username }}</h1>
<a href="{% url "logout" %}">註銷</a>
<a href="{% url "set_password" %}">修改密碼</a>
</body>
</html>
home
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>修改密碼</h1>
<form action="{% url "set_password" %}" method="post">
    {% csrf_token %}
    舊密碼:<input type="password" name="old_password">
    新密碼:<input type="password" name="new_password">
    確認密碼:<input type="password" name="r_new_password">
    <input type="submit" value="提交">
</form>
</body>
</html>
set_password

校驗用戶是否登陸

user對象的 is_authenticated()

若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
    經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name

要求:

  1  用戶登錄後才能訪問某些頁面,

  2  若是用戶沒有登陸就訪問該頁面的話直接跳到登陸頁面

  3  用戶在跳轉的登錄界面中完成登錄後,自動訪問跳轉到以前訪問的地址

方法1:

def my_view(request):
  if not request.user.is_authenticated():
    return redirect('%s?next=%s' % (settings.LOGIN_URL, request.path))

方法2:

django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_requierd()

from django.contrib.auth.decorators import login_required
      
@login_required
def my_view(request):
  ...

若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' (這個值能夠在settings文件中經過LOGIN_URL進行修改)。並傳遞  當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。

login_requierd()

    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/'  # 這裏配置成你項目登陸頁面的路由

登陸成功以後跳轉頁面實列:

前端跳轉:

用戶直接請求修改密碼的界面,由於沒有登陸因此必須先登陸,而且會記錄用戶登陸成功後須要跳轉的下一個頁面,

取到/set_password/路徑,判斷若是取到路徑就跳轉,若是沒有就跳轉指定的路徑下(首頁)

後端跳轉:

 

4、擴展auth_user表

這內置的認證系統這麼好用,可是auth_user表字段都是固定的那幾個,我在項目中無法拿來直接使用啊!

  好比,我想要加一個存儲用戶手機號的字段,怎麼辦?

  聰明的你可能會想到新建另一張表而後經過一對一和內置的auth_user表關聯,這樣雖然能知足要求可是有沒有更好的實現方式呢?

  答案是固然有了。

  咱們能夠經過繼承內置的 AbstractUser 類,來定義一個本身的Model類。django給咱們自動建立的一張user表,而若是要用auth模塊,就必需要使用(或繼承)這張表。

  這樣既能根據項目需求靈活的設計用戶表,又能使用Django強大的認證系統了。繼承表的好處是咱們能夠增長一些本身須要的字段,而且同時可使用auth模塊提供的接口、方法

複製代碼
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
複製代碼

  須要注意的是,UserInfo表裏就不須要有auth_user裏重複的字段了,好比說username以及password等,可是仍是能夠直接使用這些字段的,而且django會自動將password進行加密

  按上面的方式擴展了內置的auth_user表以後,必定要在settings.py中告訴Django,我如今使用我新定義的UserInfo表來作用戶認證。寫法以下:

# 引用Django自帶的User表,繼承使用時須要設置,這樣django就知道從咱們的app名的應用下的models文件中去查找UserInfo這張表了
AUTH_USER_MODEL = "app名.UserInfo"

  自定義認證系統默認使用的數據表以後,咱們就能夠像使用默認的auth_user表那樣使用咱們的UserInfo表了。好比:

  建立普通用戶:

UserInfo.objects.create_user(username='用戶名', password='密碼')

  建立超級用戶:

UserInfo.objects.create_superuser(username='用戶名', password='密碼')

  再次注意:

    一旦咱們指定了新的認證系統所使用的表,咱們就須要從新在數據庫中建立該表,而不能繼續直接使用原來默認的auth_user表了。

相關文章
相關標籤/搜索