• djangorestframework-simplejwt


    0. 介绍

    因为jwt官方已经停止维护,且对于django4.x不支持,所以选择simplejwt(django>=2.0)

    一定要配合权限一起使用,不然不生效

    1.使用

    1.1 安装

    先安装drf,因为jwt仅仅只是drf的一个第三方扩展且支持Django>=2.0

    1. pip3 install djangorestframework-simplejwt -i https://pypi.douban.com/simple
    2. pip3 install djangorestframework -i https://pypi.douban.com/simple

    1.2 settings.py

    1. # 修改配置
    2. INSTALLED_APPS = [
    3. 'rest_framework',
    4. ]
    5. # 修改DRF认证
    6. REST_FRAMEWORK = {
    7. 'DEFAULT_AUTHENTICATION_CLASSES': [
    8. 'rest_framework_simplejwt.authentication.JWTAuthentication', # 使用rest_framework_simplejwt(token)验证身份
    9. 'rest_framework.authentication.SessionAuthentication', # 基于用户名密码认证方式
    10. 'rest_framework.authentication.BasicAuthentication' # 基于Session认证方式
    11. ],
    12. 'DEFAULT_PERMISSION_CLASSES': [
    13. 'rest_framework.permissions.IsAuthenticated' # 默认权限为验证用户
    14. ],
    15. }
    16. # 修改simplejwt
    17. # simplejwt配置, 需要导入datetime模块
    18. SIMPLE_JWT = {
    19. # token有效时长
    20. 'ACCESS_TOKEN_LIFETIME': datetime.timedelta(minutes=30),
    21. # token刷新后的有效时间
    22. 'REFRESH_TOKEN_LIFETIME': datetime.timedelta(days=1),
    23. }

    1.3 urls.py

    1. from django.contrib import admin
    2. from django.urls import path, include
    3. # 导入 simplejwt 提供的几个验证视图类
    4. from rest_framework_simplejwt.views import (
    5. TokenObtainPairView,
    6. TokenRefreshView,
    7. TokenVerifyView
    8. )
    9. urlpatterns = [
    10. # Django 后台
    11. path('admin/', admin.site.urls),
    12. # DRF 提供的一系列身份认证的接口,用于在页面中认证身份,详情查阅DRF文档
    13. path('api/auth/', include('rest_framework.urls', namespace='rest_framework')),
    14. # 获取Token的接口
    15. path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    16. # 刷新Token有效期的接口
    17. path('api/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    18. # 验证Token的有效性
    19. path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    20. ]

    1.4 创建数据库并添加用户

    1. python manage.py makemigrations
    2. python manage.py migrate
    3. python manage.py createsuperuser

    1.5 访问路由

    1.5.1 获取token

    http://127.0.0.1/api/api/token/

     1.5.2 校验token

    http://127.0.0.1/api/token/verify/

    校验成功返回{} 

     1.5.2 刷新

    http://127.0.0.1/api/refresh/

     1.6 key介绍

    refresh:用于token失效时,刷新获得新的token

    access:就是jwt里面的token

    2. 报错AttributeError: 'str' object has no attribute 'decode' 

    (135条消息) AttributeError: ‘str‘ object has no attribute ‘decode‘_骑台风走的博客-CSDN博客https://blog.csdn.net/qq_52385631/article/details/126840588?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22126840588%22%2C%22source%22%3A%22qq_52385631%22%7D

    3. 认证的两种方式

    需要配合权限使用

    3.1 配置 

    3.1.1 url

    1. from django.urls import path, re_path
    2. from .views import ArticleListAPIView, ArticleCreateAPIView, ArticleUpdateAPIView
    3. urlpatterns = [
    4. # 图示展示(所有人)
    5. path('list/', ArticleListAPIView.as_view(), name='register'),
    6. ]

    3.1.2 view 

    1. from rest_framework.generics import ListAPIView
    2. from rest_framework.permissions import IsAuthenticated
    3. from .models import Article
    4. from .serializers import ArticleListModelSerializer
    5. class ArticleListAPIView(ListAPIView):
    6. """文章展示视图"""
    7. permission_classes = [IsAuthenticated]
    8. # select_related第一次拿数据时,就跨表拿到数据,防止多次查询
    9. queryset = Article.objects.filter(is_delete=False, is_show=True).select_related('userinfo')
    10. # print('queryset', queryset.first().__dict__)
    11. serializer_class = ArticleListModelSerializer

    3.1.3 models

    Article

    1. from django.db import models
    2. from users.models import UserInfo
    3. class Article(models.Model):
    4. title = models.CharField(verbose_name='标题', max_length=225)
    5. desc = models.TextField(verbose_name="文章简介", null=True, blank=True)
    6. content = models.TextField(verbose_name="内容", null=True, blank=True)
    7. userinfo = models.ForeignKey(verbose_name='用户', to=UserInfo, on_delete=models.DO_NOTHING,
    8. null=True, blank=True, db_constraint=False)
    9. def __str__(self):
    10. return self.title
    11. class Meta:
    12. # 联合约束
    13. unique_together = ["title", "userinfo"]
    14. # 联合索引
    15. index_together = ["title", "content"]

    UserInfo

    需要在settings,py里面配置一下
    # 注册自定义用户模型,格式:“app应用名.表名称”
    AUTH_USER_MODEL = 'users.UserInfo'
    1. from django.db import models
    2. from django.contrib.auth.models import AbstractUser
    3. class UserInfo(AbstractUser):
    4. """用户模型类"""
    5. phone = models.CharField(max_length=11, verbose_name='手机号码', blank=True, null=True, unique=True)
    6. avatar = models.ImageField(upload_to='avatar', verbose_name='用户头像', null=True, blank=True)
    7. def __str__(self):
    8. return self.username
    9. class Meta:
    10. # 联合索引,联合同步查询,提高效率
    11. index_together = ["username", "phone"]

    3.1.4 serializer

    1. from rest_framework import serializers
    2. from . import models
    3. class ArticleListModelSerializer(serializers.ModelSerializer):
    4. """
    5. 文章展示序列化器
    6. """
    7. userinfo = serializers.SerializerMethodField()
    8. class Meta:
    9. model = models.Article
    10. fields = ['id', 'title', 'desc', 'content', 'userinfo']
    11. def get_userinfo(self, obj):
    12. """返回userinfo的username"""
    13. return obj.userinfo.username

    3.2 认证

    3.2.1 第一种方式

    simplejwt的身份认证方式为:在请求的Headers里面里面添加设置参数,名称为:Authorization, 值是一个固定组成的字符串: Bearer +空格 + access, 例如:Bearer [token值]。 正确的效果如下

     3.2.1 第二种

     4.Simple JWT的默认设置

    1. # settings.py
    2. from datetime import timedelta
    3. SIMPLE_JWT = {
    4. 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), # 访问令牌的有效时间
    5. 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # 刷新令牌的有效时间
    6. 'ROTATE_REFRESH_TOKENS': False, # 若为True,则刷新后新的refresh_token有更新的有效时间
    7. 'BLACKLIST_AFTER_ROTATION': True, # 若为True,刷新后的token将添加到黑名单中,
    8. # When True,'rest_framework_simplejwt.token_blacklist',should add to INSTALLED_APPS
    9. 'ALGORITHM': 'HS256', # 对称算法:HS256 HS384 HS512 非对称算法:RSA
    10. 'SIGNING_KEY': SECRET_KEY,
    11. 'VERIFYING_KEY': None, # if signing_key, verifying_key will be ignore.
    12. 'AUDIENCE': None,
    13. 'ISSUER': None,
    14. 'AUTH_HEADER_TYPES': ('Bearer',), # Authorization: Bearer
    15. 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', # if HTTP_X_ACCESS_TOKEN, X_ACCESS_TOKEN: Bearer
    16. 'USER_ID_FIELD': 'id', # 使用唯一不变的数据库字段,将包含在生成的令牌中以标识用户
    17. 'USER_ID_CLAIM': 'user_id',
    18. # 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), # default: access
    19. # 'TOKEN_TYPE_CLAIM': 'token_type', # 用于存储令牌唯一标识符的声明名称 value:'access','sliding','refresh'
    20. #
    21. # 'JTI_CLAIM': 'jti',
    22. #
    23. # 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', # 滑动令牌是既包含到期声明又包含刷新到期声明的令牌
    24. # 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), # 只要滑动令牌的到期声明中的时间戳未通过,就可以用来证明身份验证
    25. # 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), # path('token|refresh', TokenObtainSlidingView.as_view())
    26. }

     5.自定义

    5.1自定义令牌+返回数据格式

    如果你希望在payload部分提供更多信息,比如用户的username,这时你就要自定义令牌(token)了。

     5.1.1 seralizers.py

    1. from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
    2. class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    3. """
    4. 自定义令牌
    5. """
    6. @classmethod
    7. def get_token(cls, user):
    8. token = super(MyTokenObtainPairSerializer, cls).get_token(user)
    9. # Add custom claims
    10. token['logo'] = '爱谌的缘'
    11. return token
    12. def validate(self, attrs):
    13. """
    14. 自定义返回的格式
    15. """
    16. old_data = super().validate(attrs)
    17. # refresh = self.get_token(self.user)
    18. data = {'status': 1004,
    19. 'msg': '成功',
    20. "token": old_data,
    21. # 'refresh': str(refresh),
    22. # 'access': str(refresh.access_token)
    23. }
    24. return data

    5.1.2 views.py

    1. from rest_framework_simplejwt.views import TokenObtainPairView
    2. from rest_framework.permissions import AllowAny
    3. from .serializers import MyTokenObtainPairSerializer
    4. class MyObtainTokenPairView(TokenObtainPairView):
    5. permission_classes = (AllowAny,)
    6. serializer_class = MyTokenObtainPairSerializer

    5.1.3 urls.py

    1. from django.urls import path, re_path, include
    2. from rest_framework_simplejwt.views import (
    3. TokenObtainPairView,
    4. TokenRefreshView,
    5. TokenVerifyView
    6. )
    7. from .views import MyObtainTokenPairView
    8. urlpatterns = [
    9. # 刷新Token有效期的接口
    10. path('refresh/token/', MyObtainTokenPairView.as_view(), name='token_refresh'),
    11. # 验证Token的有效性
    12. path('check/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    13. ]

    5.2 自定义认证后台(Backend)(无法使用)

    支持手机号/邮箱/账号+密码登录

    这个是自动签发token走的接口,我们一般都是手动签发,所以基本上不使用这个

    5.2.1 authenticates.py

    1. from django.contrib.auth.backends import ModelBackend
    2. from django.db.models import Q
    3. from django.contrib.auth import get_user_model
    4. # 此方法将返回当前活动的用户模型
    5. from rest_framework import serializers
    6. # User = get_user_model()
    7. UserModel = get_user_model()
    8. # print(User)
    9. class MyCustomBackend(ModelBackend):
    10. """自定义登录,支持用户名,手机,邮箱"""
    11. # print(User, '-------')
    12. def authenticate(self, request, username=None, password=None, **kwargs):
    13. # if username is None:
    14. # username = kwargs.get(UserModel.USERNAME_FIELD)
    15. try:
    16. # user = UserModel._default_manager.get_by_natural_key(username)
    17. user = UserModel.objects.get(Q(username=username) | Q(email=username) | Q(telephone=username))
    18. # print(user, '-------')
    19. except UserModel.DoesNotExist:
    20. # Run the default password hasher once to reduce the timing
    21. # difference between an existing and a nonexistent user (#20760).
    22. UserModel().set_password(password)
    23. else:
    24. if user.check_password(password) and self.user_can_authenticate(user):
    25. return user

    5.2.2 settings.py

    1. # 支持多方式登录
    2. AUTHENTICATION_BACKENDS = ['users.authenticates.MyCustomBackend']

    6. jwt解码

    1. class MyTokenObtainPairSerializer(TokenObtainPairSerializer):
    2. """
    3. 自定义令牌
    4. """
    5. def validate(self, attrs):
    6. """
    7. 自定义返回的格式
    8. """
    9. from django.conf import settings
    10. from jwt import decode as jwt_decode
    11. refresh = self.get_token(self.user)
    12. # str(refresh.access_token) 就是token
    13. decoded_data = jwt_decode(str(refresh.access_token), settings.SECRET_KEY, algorithms=["HS256"])
    14. print(decoded_data) # {'token_type': 'access', 'exp': 1663135808, 'jti': 'fd27374c6ac14d7b85d344979d812b6f', 'user_id': 1, 'logo': '爱琉的缘'}
    15. return data

    7. 手动颁发 token

    1. def _make_token(self, user):
    2. """手动签发token"""
    3. from rest_framework_simplejwt.tokens import RefreshToken
    4. refresh = RefreshToken.for_user(user)
    5. content = {
    6. 'refresh': str(refresh),
    7. 'access': str(refresh.access_token),
    8. }

    8. 自定义token异常响应

    middleware.py 中间件

    1. class ExceptionChange:
    2. def __init__(self, get_response):
    3. self.get_response = get_response
    4. def __call__(self, request):
    5. response = self.get_response(request)
    6. return response
    7. def process_template_response(self, request, response):
    8. if hasattr(response, 'data'):
    9. data = response.data
    10. # print(data)
    11. if isinstance(data, dict) is True:
    12. if "detail" in data.keys():
    13. # 用户名或密码错误
    14. if data.get("detail") == "No active account found with the given credentials":
    15. del response.data["detail"]
    16. response.data["code"] = 402
    17. response.data["msg"] = "用户名或者密码错误!"
    18. # 验证信息过期 token 过期
    19. if data.get("detail") == "此令牌对任何类型的令牌无效":
    20. del response.data["detail"]
    21. del response.data["messages"]
    22. response.data["code"] = 401
    23. response.data["msg"] = "登录已过期,请重新登录"
    24. # 未使用验证信息 未带验证信息请求
    25. if data.get("detail") == "身份认证信息未提供。": # 身份认证信息未提供。
    26. del response.data["detail"]
    27. response.data["code"] = 401
    28. response.data["msg"] = "登录已过期,请重新登录"
    29. # refresh 无效或者过期
    30. if data.get("detail") == "令牌无效或已过期": # 身份认证信息未提供。
    31. del response.data["detail"]
    32. response.data["code"] = 403
    33. response.data["msg"] = "令牌无效或已过期"
    34. return response

    在setting中配置

    1. MIDDLEWARE = [
    2. ...
    3. 'users.middleware.ExceptionChange',
    4. ]

    9. 权限

    10. 认证

    这是检测Django用户数据库的基本认证方案:

    django: 配置为AUTHENTICATION_BACKENDS,

    setting.py不写的话,AUTHENTICATION_BACKENDS默认设置为(‘django.contrib.auth.backends.ModelBackend’,),

    2. 按照 AUTHENTICATION_BACKENDS 的排列顺序,如果同样的用户名和密码在第一次就匹配了,那么Django将停止处理后面的东西      

    3. restful:  配置为 DEFAULT_AUTHENTICATION_CLASSES

    11. 本文借鉴

    (143条消息) djangorestframework-simplejwt入门教程_做我的code吧的博客-CSDN博客
    (143条消息) Django REST Framework教程(7): 如何使用JWT认证(神文多图)_大江狗的博客-CSDN博客
    djangorestframework-simplejwt简单使用 - 简书 (jianshu.com)(143条消息) DjangoRestFramework 使用 simpleJWT 登陆认证_PFFFei的博客-CSDN博客

  • 相关阅读:
    bootz 启动 kernel
    《论文阅读》SalesBot: Transitioning from Chit-Chat to Task-Oriented Dialogues
    四、ref与DOM-findDomNode-unmountComponentAtNode
    Linux终端的读写(1)
    Kotlin基础学习 17
    【MySQL】Navicat15 安装
    FFplay文档解读-38-视频过滤器十三
    基于SpringBoot开发的停车位管理系统(调用百度地图api)
    使用sa-Token多账户认证的时候出现错误未能获取对应StpLogic,type=admin
    RPA的尽头是超自动化?
  • 原文地址:https://blog.csdn.net/qq_52385631/article/details/126840331