因为jwt官方已经停止维护,且对于django4.x不支持,所以选择simplejwt(django>=2.0)
一定要配合权限一起使用,不然不生效
先安装drf,因为jwt仅仅只是drf的一个第三方扩展且支持Django>=2.0
- pip3 install djangorestframework-simplejwt -i https://pypi.douban.com/simple
- pip3 install djangorestframework -i https://pypi.douban.com/simple
- # 修改配置
- INSTALLED_APPS = [
- 'rest_framework',
- ]
-
- # 修改DRF认证
- REST_FRAMEWORK = {
- 'DEFAULT_AUTHENTICATION_CLASSES': [
- 'rest_framework_simplejwt.authentication.JWTAuthentication', # 使用rest_framework_simplejwt(token)验证身份
- 'rest_framework.authentication.SessionAuthentication', # 基于用户名密码认证方式
- 'rest_framework.authentication.BasicAuthentication' # 基于Session认证方式
- ],
- 'DEFAULT_PERMISSION_CLASSES': [
- 'rest_framework.permissions.IsAuthenticated' # 默认权限为验证用户
- ],
- }
- # 修改simplejwt
- # simplejwt配置, 需要导入datetime模块
- SIMPLE_JWT = {
- # token有效时长
- 'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),
- # token刷新后的有效时间
- 'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
- }
- from django.contrib import admin
- from django.urls import path, include
-
- # 导入 simplejwt 提供的几个验证视图类
- from rest_framework_simplejwt.views import (
- TokenObtainPairView,
- TokenRefreshView,
- TokenVerifyView
- )
-
- urlpatterns = [
- # Django 后台
- path('admin/', admin.site.urls),
- # DRF 提供的一系列身份认证的接口,用于在页面中认证身份,详情查阅DRF文档
- path('api/auth/', include('rest_framework.urls', namespace='rest_framework')),
- # 获取Token的接口
- path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
- # 刷新Token有效期的接口
- path('api/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
- # 验证Token的有效性
- path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
- ]
- python manage.py makemigrations
- python manage.py migrate
- python manage.py createsuperuser
http://127.0.0.1/api/api/token/
http://127.0.0.1/api/token/verify/
校验成功返回{}
http://127.0.0.1/api/refresh/
refresh:用于token失效时,刷新获得新的token
access:就是jwt里面的token
需要配合权限使用
- from django.urls import path, re_path
-
- from .views import ArticleListAPIView, ArticleCreateAPIView, ArticleUpdateAPIView
-
- urlpatterns = [
- # 图示展示(所有人)
- path('list/', ArticleListAPIView.as_view(), name='register'),
-
- ]
-
- from rest_framework.generics import ListAPIView
- from rest_framework.permissions import IsAuthenticated
-
- from .models import Article
-
- from .serializers import ArticleListModelSerializer
-
-
- class ArticleListAPIView(ListAPIView):
- """文章展示视图"""
- permission_classes = [IsAuthenticated]
- # select_related第一次拿数据时,就跨表拿到数据,防止多次查询
- queryset = Article.objects.filter(is_delete=False, is_show=True).select_related('userinfo')
- # print('queryset', queryset.first().__dict__)
- serializer_class = ArticleListModelSerializer
Article
- from django.db import models
-
- from users.models import UserInfo
-
-
- class Article(models.Model):
- title = models.CharField(verbose_name='标题', max_length=225)
- desc = models.TextField(verbose_name="文章简介", null=True, blank=True)
- content = models.TextField(verbose_name="内容", null=True, blank=True)
- userinfo = models.ForeignKey(verbose_name='用户', to=UserInfo, on_delete=models.DO_NOTHING,
- null=True, blank=True, db_constraint=False)
-
- def __str__(self):
- return self.title
-
- class Meta:
- # 联合约束
- unique_together = ["title", "userinfo"]
- # 联合索引
- index_together = ["title", "content"]
UserInfo
需要在settings,py里面配置一下 # 注册自定义用户模型,格式:“app应用名.表名称” AUTH_USER_MODEL = 'users.UserInfo'
- from django.db import models
- from django.contrib.auth.models import AbstractUser
-
-
- class UserInfo(AbstractUser):
- """用户模型类"""
- phone = models.CharField(max_length=11, verbose_name='手机号码', blank=True, null=True, unique=True)
- avatar = models.ImageField(upload_to='avatar', verbose_name='用户头像', null=True, blank=True)
-
- def __str__(self):
- return self.username
-
- class Meta:
- # 联合索引,联合同步查询,提高效率
- index_together = ["username", "phone"]
- from rest_framework import serializers
-
- from . import models
-
-
- class ArticleListModelSerializer(serializers.ModelSerializer):
- """
- 文章展示序列化器
- """
- userinfo = serializers.SerializerMethodField()
-
- class Meta:
- model = models.Article
- fields = ['id', 'title', 'desc', 'content', 'userinfo']
-
- def get_userinfo(self, obj):
- """返回userinfo的username"""
- return obj.userinfo.username
simplejwt的身份认证方式为:在请求的Headers里面里面添加设置参数,名称为:Authorization, 值是一个固定组成的字符串: Bearer +空格 + access, 例如:Bearer [token值]。 正确的效果如下
- # settings.py
-
- from datetime import timedelta
-
- SIMPLE_JWT = {
- 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), # 访问令牌的有效时间
- 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # 刷新令牌的有效时间
-
- 'ROTATE_REFRESH_TOKENS': False, # 若为True,则刷新后新的refresh_token有更新的有效时间
- 'BLACKLIST_AFTER_ROTATION': True, # 若为True,刷新后的token将添加到黑名单中,
- # When True,'rest_framework_simplejwt.token_blacklist',should add to INSTALLED_APPS
-
- 'ALGORITHM': 'HS256', # 对称算法:HS256 HS384 HS512 非对称算法:RSA
- 'SIGNING_KEY': SECRET_KEY,
- 'VERIFYING_KEY': None, # if signing_key, verifying_key will be ignore.
- 'AUDIENCE': None,
- 'ISSUER': None,
-
- 'AUTH_HEADER_TYPES': ('Bearer',), # Authorization: Bearer
- 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', # if HTTP_X_ACCESS_TOKEN, X_ACCESS_TOKEN: Bearer
- 'USER_ID_FIELD': 'id', # 使用唯一不变的数据库字段,将包含在生成的令牌中以标识用户
- 'USER_ID_CLAIM': 'user_id',
-
- # 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), # default: access
- # 'TOKEN_TYPE_CLAIM': 'token_type', # 用于存储令牌唯一标识符的声明名称 value:'access','sliding','refresh'
- #
- # 'JTI_CLAIM': 'jti',
- #
- # 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', # 滑动令牌是既包含到期声明又包含刷新到期声明的令牌
- # 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), # 只要滑动令牌的到期声明中的时间戳未通过,就可以用来证明身份验证
- # 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), # path('token|refresh', TokenObtainSlidingView.as_view())
- }
如果你希望在payload部分提供更多信息,比如用户的username,这时你就要自定义令牌(token)了。
- from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
-
-
-
- class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
- """
- 自定义令牌
- """
- @classmethod
- def get_token(cls, user):
- token = super(MyTokenObtainPairSerializer, cls).get_token(user)
-
- # Add custom claims
- token['logo'] = '爱谌的缘'
- return token
-
- def validate(self, attrs):
- """
- 自定义返回的格式
- """
- old_data = super().validate(attrs)
-
- # refresh = self.get_token(self.user)
- data = {'status': 1004,
- 'msg': '成功',
- "token": old_data,
- # 'refresh': str(refresh),
- # 'access': str(refresh.access_token)
- }
-
- return data
- from rest_framework_simplejwt.views import TokenObtainPairView
- from rest_framework.permissions import AllowAny
-
- from .serializers import MyTokenObtainPairSerializer
-
-
-
-
- class MyObtainTokenPairView(TokenObtainPairView):
- permission_classes = (AllowAny,)
- serializer_class = MyTokenObtainPairSerializer
- from django.urls import path, re_path, include
- from rest_framework_simplejwt.views import (
- TokenObtainPairView,
- TokenRefreshView,
- TokenVerifyView
- )
- from .views import MyObtainTokenPairView
-
- urlpatterns = [
- # 刷新Token有效期的接口
- path('refresh/token/', MyObtainTokenPairView.as_view(), name='token_refresh'),
- # 验证Token的有效性
- path('check/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
-
-
-
- ]
支持手机号/邮箱/账号+密码登录
这个是自动签发token走的接口,我们一般都是手动签发,所以基本上不使用这个
- from django.contrib.auth.backends import ModelBackend
- from django.db.models import Q
- from django.contrib.auth import get_user_model
-
- # 此方法将返回当前活动的用户模型
- from rest_framework import serializers
-
- # User = get_user_model()
- UserModel = get_user_model()
-
-
- # print(User)
-
-
- class MyCustomBackend(ModelBackend):
- """自定义登录,支持用户名,手机,邮箱"""
- # print(User, '-------')
-
- def authenticate(self, request, username=None, password=None, **kwargs):
- # if username is None:
- # username = kwargs.get(UserModel.USERNAME_FIELD)
- try:
- # user = UserModel._default_manager.get_by_natural_key(username)
- user = UserModel.objects.get(Q(username=username) | Q(email=username) | Q(telephone=username))
- # print(user, '-------')
- except UserModel.DoesNotExist:
- # Run the default password hasher once to reduce the timing
- # difference between an existing and a nonexistent user (#20760).
- UserModel().set_password(password)
- else:
- if user.check_password(password) and self.user_can_authenticate(user):
- return user
- # 支持多方式登录
- AUTHENTICATION_BACKENDS = ['users.authenticates.MyCustomBackend']
- class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
- """
- 自定义令牌
- """
-
-
-
- def validate(self, attrs):
- """
- 自定义返回的格式
- """
-
- from django.conf import settings
-
- from jwt import decode as jwt_decode
- refresh = self.get_token(self.user)
- # str(refresh.access_token) 就是token
- decoded_data = jwt_decode(str(refresh.access_token), settings.SECRET_KEY, algorithms=["HS256"])
- print(decoded_data) # {'token_type': 'access', 'exp': 1663135808, 'jti': 'fd27374c6ac14d7b85d344979d812b6f', 'user_id': 1, 'logo': '爱琉的缘'}
- return data
- def _make_token(self, user):
- """手动签发token"""
- from rest_framework_simplejwt.tokens import RefreshToken
- refresh = RefreshToken.for_user(user)
- content = {
- 'refresh': str(refresh),
- 'access': str(refresh.access_token),
- }
- class ExceptionChange:
-
- def __init__(self, get_response):
- self.get_response = get_response
-
- def __call__(self, request):
- response = self.get_response(request)
- return response
-
- def process_template_response(self, request, response):
- if hasattr(response, 'data'):
- data = response.data
- # print(data)
- if isinstance(data, dict) is True:
- if "detail" in data.keys():
- # 用户名或密码错误
- if data.get("detail") == "No active account found with the given credentials":
- del response.data["detail"]
-
- response.data["code"] = 402
- response.data["msg"] = "用户名或者密码错误!"
-
- # 验证信息过期 token 过期
- if data.get("detail") == "此令牌对任何类型的令牌无效":
- del response.data["detail"]
- del response.data["messages"]
- response.data["code"] = 401
- response.data["msg"] = "登录已过期,请重新登录"
-
- # 未使用验证信息 未带验证信息请求
- if data.get("detail") == "身份认证信息未提供。": # 身份认证信息未提供。
- del response.data["detail"]
- response.data["code"] = 401
- response.data["msg"] = "登录已过期,请重新登录"
-
- # refresh 无效或者过期
- if data.get("detail") == "令牌无效或已过期": # 身份认证信息未提供。
- del response.data["detail"]
- response.data["code"] = 403
- response.data["msg"] = "令牌无效或已过期"
-
- return response
-
- MIDDLEWARE = [
- ...
- 'users.middleware.ExceptionChange',
- ]
这是检测Django用户数据库的基本认证方案:
django: 配置为AUTHENTICATION_BACKENDS,
setting.py不写的话,AUTHENTICATION_BACKENDS默认设置为(‘django.contrib.auth.backends.ModelBackend’,),
2. 按照 AUTHENTICATION_BACKENDS 的排列顺序,如果同样的用户名和密码在第一次就匹配了,那么Django将停止处理后面的东西
3. restful: 配置为 DEFAULT_AUTHENTICATION_CLASSES
(143条消息) djangorestframework-simplejwt入门教程_做我的code吧的博客-CSDN博客
(143条消息) Django REST Framework教程(7): 如何使用JWT认证(神文多图)_大江狗的博客-CSDN博客
djangorestframework-simplejwt简单使用 - 简书 (jianshu.com)(143条消息) DjangoRestFramework 使用 simpleJWT 登陆认证_PFFFei的博客-CSDN博客