因为如果使用Basic认证
,每次用户都输入用户名和密码,如果使用session认证
,需要在数据库中生成一张session表,保存session信息,增加了服务器的开销.因此提出了使用jwt认证,jwt认证在服务器存储了加密方式和解密方式,并没有存储任何数据,服务器中对数据进行处理远远高于在数据库中进行处理.
这部分笔记有参考:https://blog.csdn.net/aaaaaaaaanjjj/article/details/122968746
pip install djangorestframework-simplejwt
需要使用的是rest_framework_simplejwt
下面authentication.py
下面的JWTAuthentication类
于是将上面的路径复制到settings中
# 全局认证 优秀级高于视图类中的配置
'DEFAULT_AUTHENTICATION_CLASSES': [
# simple-jwt认证
'rest_framework_simplejwt.authentication.JWTAuthentication'
],
# simplejwt配置, 需要导入datetime模块
SIMPLE_JWT = {
# token有效时长
'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),
# token刷新后的有效时间
'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
}
在我的Django项目的urls
代码中:
from django.contrib import admin
from django.urls import path, include
# 引入DRF默认自带的路由
from rest_framework import routers
from shop.views import *
# 引入API文档路由
from rest_framework.documentation import include_docs_urls
from user.views import UserViewSets
# 引入rest_framework_simplejwt路由
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
TokenVerifyView
)
router=routers.DefaultRouter()
# 可以通过router默认路由注册资源
router.register('categorys',CategoryViewSets)
router.register('goods',GoodsViewSets)
router.register('users',UserViewSets)
router.register('orders',OrderViewSets)
urlpatterns = [
path('admin/', admin.site.urls),
# 配置RESTFulAPI
path('api/v1/',include(router.urls)),
# API文档地址
path('api/v1/docs',include_docs_urls(title="RestFulAPI",description="RestFulAPI v1")),
# rest_framework_simplejwt路由
# 先通过用户名密码 得到Token Vue将refresh以及access保存>通过access请求服务器 >通过refresh获取新的token
# 获取Token接口
path('api/token/',TokenObtainPairView.as_view(),name='login'),
# 获取Token有效期的接口
path('api/refresh/',TokenRefreshView.as_view(),name="refresh"),
# 获取Token的有效性
path('api/token/verify',TokenVerifyView.as_view(),name="token_verify"),
# 为了在DRF路由调试界面能够使用用户相关功能需要引入以下路由
path('',include('rest_framework.urls'))
]
第一次使用post请求,空参请求http://127.0.0.1:8000/api/token/
,则会提示你需要提交账户名和密码:
则需要在post请求中填写提交的信息:
提交完用户名和密码之后,则会返回refresh
和access
此时则需要,将之前登入时候生成的access的token复制过来认证,认证通过之后就可以查询信息了.
过了5分钟之后再去请求category的查询,则此时会显示已经access的请求已经过期
,则此时就需要refresh的到的access
.
进入refresh
的接口,将前面第一次获取token请求得到的refresh
的token复制到refresh
请求接口中,得到一个新的access
token,将其复制到category
请求中的Bearer Token
中,则又可以继续请求了.
此时的请求成功,可以显示所有查询结果:
如果想像百度等这一种web网页,可以实现手机/邮箱/用户名都可以登入,则就需要使用到自定义认证:
在user
app目录下面创建一个authbackend.py
,然后在里面创建一个自定义认证类,继承django.contrib.auth.backends.BaseBackend
重写authenticate
函数:
原始的只实现用户名密码登入
"""
实现自定义认证类(登入 内容)
如果想自定义认证,就需要继承Django原生的auth,重写django认证方式
"""
from django.contrib.auth.backends import BaseBackend
from user.models import User
from django.db.models import Q
class MyLoginBackend(BaseBackend):
def authenticate(self, request, **kwargs):
"""
@param request:
@param kwargs:认证参数
@return:如果认证成功返回认证用户 否则返回None
"""
username=kwargs["username"]
password=kwargs["password"]
# 根据用户名查到第一个这个user对象
user=User.objects.filter(username=username).first()
# 对用户密码进行验证
b=user.check_password(password)
if b:
return user
else:
return None
# 注册自定义认证类 应用名.文件名.认证类名
AUTHENTICATION_BACKENDS=('user.authbackend.MyLoginBackend',)
在获取token接口中测试,只要用户名和密码正确,直接就可以返回对应的token
"""
实现自定义认证类(登入 内容)
如果想自定义认证,就需要继承Django原生的auth,重写django认证方式
"""
from django.contrib.auth.backends import BaseBackend
from user.models import User
from django.db.models import Q
class MyLoginBackend(BaseBackend):
def authenticate(self, request, **kwargs):
"""
@param request:
@param kwargs:认证参数
@return:如果认证成功返回认证用户 否则返回None
"""
username=kwargs["username"]
password=kwargs["password"]
# 使用Q查询,可以连接多个查询语句,在这里可以使用Q查询,先后通过邮箱,电话查询用户信息
user=User.objects.filter(Q(username=username)|Q(email=username)|Q(mobile=username)).first()
if user:
# 对用户密码进行验证
b=user.check_password(password)
if b:
return user
else:
return None
else:
return None
这部分类容参考:https://blog.csdn.net/qq_40132294/article/details/125851155
需求:需要返回的响应中包括id,username,token
控制返回值的配置文件在D:\anaconda\envs\Lib\site-packages\rest_framework_simplejwt\settings.py
,因此我们只需要重新写一个代码,替代系统自带的方法.
源码中的序列化类,继承于TokenObtainSerializer序列化类
,然后在data字典中添加了refresh
和access
两个字段.因此我们想要修改返回值的时候,主要是修改data中的内容.
user\seralizers
代码:
class TokenObtainPairSerializer(TokenObtainSerializer):
token_class = RefreshToken
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data["token"] = str(refresh.access_token) #也就是原来的access
data['user_id'] = str(self.user.id)
data['user_name'] = str(self.user.username)
if api_settings.UPDATE_LAST_LOGIN:
update_last_login(None, self.user)
return data
修改项目的settings
配置文件:
from datetime import timedelta
# simplejwt配置, 需要导入datetime模块
SIMPLE_JWT = {
# token有效时长
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
# token刷新后的有效时间
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
"TOKEN_OBTAIN_SERIALIZER":"user.serializers.TokenObtainPairSerializer"
}