0. 介绍

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

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

1.使用

1.1 安装

先安装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

1.2 settings.py

# 修改配置

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),

}

1.3 urls.py

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'),

]

1.4 创建数据库并添加用户

python manage.py makemigrations

python manage.py migrate

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

from django.urls import path, re_path

from .views import ArticleListAPIView, ArticleCreateAPIView, ArticleUpdateAPIView

urlpatterns = [

# 图示展示(所有人)

path('list/', ArticleListAPIView.as_view(), name='register'),

]

3.1.2 view 

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

3.1.3 models

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"]

3.1.4 serializer

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

3.2 认证

3.2.1 第一种方式

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

 3.2.1 第二种

 4.Simple JWT的默认设置

# 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())

}

 5.自定义

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

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

 5.1.1 seralizers.py

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

5.1.2 views.py

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

5.1.3 urls.py

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'),

]

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

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

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

5.2.1 authenticates.py

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

5.2.2 settings.py

# 支持多方式登录

AUTHENTICATION_BACKENDS = ['users.authenticates.MyCustomBackend']

6. jwt解码

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

7. 手动颁发 token

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),

}

8. 自定义token异常响应

middleware.py 中间件

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

在setting中配置

MIDDLEWARE = [

...

'users.middleware.ExceptionChange',

]

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博客

推荐阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: