1. 程式人生 > 實用技巧 >django_restfreamwork 4 JWT認證元件

django_restfreamwork 4 JWT認證元件

DRF【jwt認證元件】

前後端分離專案都用token認證

前後端不分離的專案都用cookie和session認證

通用配置

總路由

from django.urls import path, include
import xadmin
xadmin.autodiscover()
# version模組自動註冊需要版本控制的 Model
from xadmin.plugins import xversion
xversion.register_models()

urlpatterns = [
    # path('admin/', admin.site.urls),
    path(r'xadmin/', xadmin.site.urls),
    path('books/', include("SerDemo.urls")),
]

模型

from django.db import models

__all__ = ["Book", "Publisher", "Author"]

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name="圖書名稱")
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    category = models.IntegerField(choices=CHOICES, verbose_name="圖書的類別")
    pub_time = models.DateField(verbose_name="圖書的出版日期")

    publisher = models.ForeignKey(to="Publisher", on_delete=None)
    author = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.title

    class Meta:
        db_table = "book"
        verbose_name_plural = "圖書表"

class Publisher(models.Model):
    title = models.CharField(max_length=32, verbose_name="出版社的名稱")

    def __str__(self):
        return self.title

    class Meta:
        db_table = "publisher"
        verbose_name_plural = "出版社表"

class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name="作者的姓名")

    def __str__(self):
        return self.name

    class Meta:
        db_table = "author"
        verbose_name_plural = "作者表"

認證準備工作

新建子應用user

python3 manage.py startapp user

註冊子應用

INSTALLED_APPS = [
  	...
    ...
		...
    #子應用
    'user',
]

建立使用者模型類models.py

user/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
    """使用者模型類"""
    # 如果手機設定 IntegerField的話,只有10位數字,並且有正有負
    # 手機號不需要計算,所以不需要設定int型別,設定字串即可
    mobile = models.CharField(max_length=15, unique=True, verbose_name='手機號')
    avatar = models.ImageField(upload_to="avator",null=True,blank=True,verbose_name="使用者頭像")
    class Meta:
        db_table = 'users'
        verbose_name = '使用者'
        verbose_name_plural = verbose_name

註冊使用者模型類

自定義的使用者模型類還不能直接被Django的認證系統所識別,需要在配置檔案中告知Django認證系統使用自定義的模型類。

settings.py

#註冊自定義user模型 --> '子應用名.models類名'
AUTH_USER_MODEL = 'user.User'

資料庫遷移

python3 manage.py makemigrations
python3 manage.py migrate

若報錯,解決方案

如果在第一次資料遷移以後,才設定 AUTH_USER_MODEL 自定義使用者模型,則會報錯。解決方案如下:

0. 先把現有的資料庫匯出備份,然後清掉資料庫 中 所有的資料表,不刪庫,只刪除下面所有的表。
1. 把開發者建立的所有子應用apps/下面的migrations目錄下除了__init__.py以外的所有遷移檔案,只要涉及到使用者的,一律刪除
2. 把django.contrib.admin.migrations目錄下除了__init__.py以外的所有遷移檔案,全部刪除。
3. 把django.contrib.auth.migrations目錄下除了__init__.py以外的所有遷移檔案,全部刪除。
4. 把reversion.migrations目錄下除了__init__.py以外的所有遷移檔案,全部刪除。
5. 把xadmin.migrations目錄下除了__init__.py以外的所有遷移檔案,全部刪除。
6. 接下來,重新執行資料遷移,回顧第0步中的資料,以後如果要修改使用者相關資料,不需要重複本次操作,直接資料遷移即可。

python manage.py makemigrations
python manage.py migrate

7. 錄入備份的資料
解決步驟0-6

錄入備份資料

  • 錄入之前先遷移資料庫,先新建表之後在錄入資料

重新建立xadmin超級使用者

python3 manage.py createsuperuser
使用者名稱 (leave blank to use 'wangjunxiang'): root
電子郵件地址: [email protected]
Password: 123123
Password (again): 123123
密碼長度太短。密碼必須包含至少 8 個字元。
這個密碼太常見了。
密碼只包含數字。
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

實現JWT認證

安裝&配置 JWT

pip3 install djangorestframework-jwt -i https://pypi.tuna.tsinghua.edu.cn/simple

settings.py

REST_FRAMEWORK = {
    #jwt登入認證
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),
}

# 設定jwt token認證有效期
import datetime
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),#可以設定seconds minutes hours days weeks
}

總路由分發子路由

urls.py

urlpatterns = [
		...
    path('user/', include('user.urls')),
    # include 的值必須是 模組名.urls 格式,字串中間只能出現一個圓點
]

新建子路由

新建檔案 user/urls.py

from rest_framework_jwt.views import obtain_jwt_token
from django.urls import path
urlpatterns = [
    path(r'login/', obtain_jwt_token),
]

測試認證

如果什麼都不帶 直接傳送 127.0.0.1:8000/user/login/ 的 post 請求,會返回

如果傳送post請求 帶如下引數 ,則返回token,需要前端將token存在瀏覽器中,這樣下次繼續訪問,會帶著這個token 和其他引數去訪問,後端先接收到token驗證成功後,就會轉給對應的介面響應需要的資料

這個是之前建立超級使用者的賬號和密碼

{
	"username": "root",
	"password": "123123"
}

設定jwt返回自定義資料

jwt返回自定義資料utils.py

新建 user/utils.py 作為 user通用的

  • 當然如果公司有其他需求,需要返回使用者頭像等其他的資料
  • 修改這個函式的返回值即可,比如返回 資料庫中使用者頭像的url欄位
  • 返回值全都自定義
def jwt_response_payload_handler(token, user=None, request=None):
    """
    自定義jwt認證成功返回資料
    token:本次登入成功後,返回的jwt資料
    user:本次登陸後,從資料庫查詢到的使用者模型資訊
    request:本次客戶端的請求物件,
    """
    return {
        # token 就是前段請求 生成 儲存在客戶端的的token資料
        'token': token,
        # user.id 和 user.username 都是從user使用者資料庫模型中的資料庫.欄位獲取
        'id': user.id,
        'username': user.username
    }

配置jwt返回自定義資料生效

settings/dev.py

在 JWT_AUTH 中新增

#JWT_AUTH = {
    ....
  	....
    # 設定返回資料的格式
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.jwt_response_payload_handler',
#}

測試返回資料

同樣攜帶json的賬戶和密碼資訊 post 請求 127.0.0.1:8000/user/login/

{
	"username": "root",
	"password": "123123"
}

可以返回自定義資料

jwt的多條件登入

例如手機號登入

思路

  • JWT擴充套件的登入檢視,在收到使用者名稱與密碼時,也是呼叫Django的認證系統中提供的authenticate()來檢查使用者名稱與密碼是否正確。

  • 可以通過修改Django認證系統的認證後端(主要是authenticate方法)來支援登入賬號既可以是使用者名稱也可以是手機號。

修改Django認證系統的認證後端需要繼承django.contrib.auth.backends.ModelBackend,並重寫authenticate方法

authenticate(self, request, username=None, password=None, **kwargs)
方法的引數說明:
- request 本次認證的請求物件
- username 本次認證提供的使用者賬號
- password 本次認證提供的密碼

想讓使用者既可以 使用者名稱登入,也可以 手機號登入,那麼對於authenticate方法而言,前端既可以把使用者名稱或者手機號傳後端驗證,但是都存在username變數中,也就是username有可能是使用者名稱,也有可能是手機號

重寫authenticate方法的思路

  • 根據username引數查詢使用者User物件,username引數可能是使用者名稱,也可能是手機號

  • 若查詢到User物件,呼叫User物件的check_password方法檢查密碼是否正確

新增自定義認證方法utils.py

users/utils.py中新增:

# def jwt_response_payload_handler(token, user=None, request=None):
#     """
#     自定義jwt認證成功返回資料
#     token:本次登入成功後,返回的jwt資料
#     user:本次登陸後,從資料庫查詢到的使用者模型資訊
#     request:本次客戶端的請求物件,
#     """
#     return {
#         # token 就是前段請求 生成 儲存在客戶端的的token資料
#         'token': token,
#         # user.id 和 user.username 都是從user使用者資料庫模型中的資料庫.欄位獲取
#         'id': user.id,
#         'username': user.username
#     }
# 

#如果還要加用郵箱登入吧mobile改為email就行
from .models import User
from django.db.models import Q      #Q物件用來做多條件查詢
from django.contrib.auth.backends import ModelBackend
def get_user_by_account(account):
    """
    根據帳號獲取user物件
    :param account: 賬號,可以是使用者名稱username,也可以是手機號mobile,或者其他的資料
    :return: User物件 或者 None
    """
    try:

        # Q物件多條件查詢, '|' 代表或者
        user = User.objects.filter( Q(username=account) | Q(mobile=account)).first()
    except User.DoesNotExist:
        return None
    else:
        return user

class UsernameMobileAuthBackend(ModelBackend):	#繼承ModelBackend
    """ 
    	自定義使用者名稱或手機號認證
    """
    def authenticate(self, request, username=None, password=None, **kwargs):
        user = get_user_by_account(username)
        # user模型下有check_password方法比較 ,user.is_authenticated用來驗證許可權
        if user is not None and user.check_password(password) and user.is_authenticated:
            return user
        else:
            return None

註冊自定義認證方式生效

settings.py

AUTHENTICATION_BACKENDS = [
    'user.utils.UsernameMobileAuthBackend',
]

測試多條件登入

之前是post請求 127.0.0.1:8000/user/login/ 介面

攜帶username 和 password,現在將username的root 換為了手機號,同樣可以返回token