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