【DRF】-- SimpleJWT

DRF Jwt 学习

环境

  1. python3.9
  2. Django                         4.2.7
    djangorestframework            3.14.0
    djangorestframework-simplejwt  5.3.0

simplejwt官网地址

为什么要使用 simplejwt

因为jwt官方已经停止维护,且对于django4.x不支持

djangorestframework-simplejwt 是一个 Django REST framework 的第三方插件,它提供了一种简单的 JSON Web Token(JWT)身份验证方式。JWT 是一种开放标准(RFC 7519),用于在网络应用间传递声明,以便于对用户进行身份验证和授权。
JWT 由三部分组成:头部、载荷和签名。
	头部包含了加密算法和类型信息,
	载荷包含了用户信息和其他声明,
	签名则是对头部和载荷进行签名的结果。

djangorestframework-simplejwt 的原理是在用户登录成功后,生成一个 JWT 并返回给客户端。
客户端在后续请求中将 JWT 放在请求头或请求参数中,服务端通过验证 JWT 的签名和有效期来验证用户身份。具体流程如下:
    用户使用用户名和密码进行登录,服务端验证用户信息是否正确。
    如果验证成功,服务端生成一个 JWT 并返回给客户端。
    客户端在后续请求中将 JWT 放在请求头或请求参数中。
    服务端从请求中获取 JWT,并验证 JWT 的签名和有效期。
    如果验证成功,服务端允许用户访问请求的资源。
    djangorestframework-simplejwt 提供了一些可配置的选项,例如 JWT 的有效期、刷新令牌的有效期、签名算法等。它还提供了一些视图类和装饰器,用于在 Django REST framework 中实现 JWT 身份验证。使用 djangorestframework-simplejwt 可以简化身份验证的实现,并提高应用程序的安全性。
    
    
    
它由三部分构成:
Header(头部):通常包含token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)以及其他信息。
Payload(载荷):是JWT的主体,通常包含用户主键、用户名、签发时客户信息(设备号、地址)、过期时间等。
注意:因为BASE64是可逆的,所以不要在JWT的payload或header中放置敏感信息,除非它们是加密的!
Signature(签名):是对上面两部分编码后的内容再进行加密后得到的密文。即:base64(Header)+"."+base64(Payload)通过SHA256加密后,得到的密文就是签名。
最后,将上面三部分通过.连接起来就得到了JWT。


# 得到JWT后,将jwt存在client, 之后每次需要认证的请求,都要把jwt发送过阿里(可放在header的Authorization)

创建一个django程序

django-admin startproject jwt_demo
cd jwt_demo
python3 manage.py startapp app

配置 jwt

jwt_demo/settings.py

INSTALLED_APPS = [
	'....'
    'rest_framework',
    'app',
]

下面内容直接放到文件最后面即可!

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        # 使用 simplejwt 认证
        "rest_framework_simplejwt.authentication.JWTAuthentication",
        # 基于 session 认证
        "rest_framework.authentication.SessionAuthentication",
    ),
    "DEFAULT_PERMISSION_CLASSES": [
        # 只有经过身份认证确定用户身份才能访问
        "rest_framework.permissions.IsAuthenticated",  
    ],
}

from datetime import timedelta

SIMPLE_JWT = {
    # token有效时长
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=1440),
    # token刷新后的有效时间
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    # 设置前缀
    "AUTH_HEADER_TYPES": ("JWT",),
    # 若为True,则刷新后新的refresh_token有更新的有效时间
    "ROTATE_REFRESH_TOKENS": True,
}

配置 url

jwt_demo/urls.py

可参考官网

from django.urls import path, re_path, include
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView, 
)

urlpatterns = [
    # 目的是为了提供一组默认的 URL 路由和视图,用于处理身份验证和授权相关的功能。
    re_path(
                r"^api-auth/", include("rest_framework.urls", namespace="rest_framework")
            ),
    # 获取Token的接口
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    # 刷新Token有效期的接口
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 验证Token的有效性
    path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

创建一个用户

python3 manage.py createsuperuser
# 执行之后填写对应的信息即可

# 创建成功会提示
Superuser created successfully.

启动程序

python3 manage.py runserver
# 默认端口是 8000

postman 测试

测试接口: /api/token
"""
区别:

Access token 是用于验证用户身份和授权用户访问受保护资源的短期令牌。
Refresh token 是用于获取新的 access token 的长期令牌。
使用方式:

客户端在登录成功后,服务器会返回一个 access token 和一个 refresh token。
客户端在每个请求中将 access token 作为身份验证凭证发送给服务器,以验证用户身份和授权访问受保护资源。
当 access token 过期时,客户端可以使用 refresh token 向服务器请求新的 access token,而无需重新登录。
服务器验证 refresh token 的有效性后,生成并返回一个新的 access token 给客户端。
客户端使用新的 access token 继续访问受保护资源。
简而言之,access token 用于验证身份和授权访问资源,refresh token 用于获取新的 access token。客户端在每个请求中使用 access token,当 access token 过期时使用 refresh token 获取新的 access token。这样可以保持用户的登录状态,并避免频繁重新登录。
"""

image.png

测试接口: /api/token/refresh/

image.png

测试接口: /api/token/verify/

image.png

自定义令牌

创建表结构

app/models.py

from django.db import models

# Create your models here.

from django.contrib.auth.models import AbstractUser



class Users(AbstractUser):
    username = models.CharField(max_length=150, unique=True, db_index=True, verbose_name="用户账号",
                                help_text="用户账号")
    mobile = models.CharField(max_length=255, verbose_name="电话", null=True, blank=True, help_text="电话")

    GENDER_CHOICES = (
        (0, "未知"),
        (1, "男"),
        (2, "女"),
    )
    gender = models.IntegerField(
        choices=GENDER_CHOICES, default=0, verbose_name="性别", null=True, blank=True, help_text="性别"
    )
    avatar = models.ImageField(upload_to='avatar', verbose_name='用户头像', null=True, blank=True)

    role = models.ManyToManyField(to="Role", blank=True, verbose_name="关联角色", db_constraint=False,
                                  help_text="关联角色")
    class Meta:
        db_table = "system_users"
        verbose_name = "用户表"
        verbose_name_plural = verbose_name



class Role(models.Model):
    name = models.CharField(max_length=64, verbose_name="角色名称", help_text="角色名称")
    key = models.CharField(max_length=64, unique=True, verbose_name="权限字符", help_text="权限字符")
    sort = models.IntegerField(default=1, verbose_name="角色顺序", help_text="角色顺序")
    status = models.BooleanField(default=True, verbose_name="角色状态", help_text="角色状态")

    class Meta:
        db_table = "system_role"
        verbose_name = "角色表"
        verbose_name_plural = verbose_name
        ordering = ("sort",)

jwt_demo/settings.py

# 这个 'app' 是你创建应用的名字
AUTH_USER_MODEL = "app.Users"

生成表

python3 manage.py makemigrations
python3 manage.py migrate

配置路由

jwt_demo/urls.py

from app.views import LoginView
from django.urls import path, re_path, include

urlpatterns = [

    re_path(
            r"^api-auth/", include("rest_framework.urls", namespace="rest_framework")
           ),
    path('api/login/', LoginView.as_view(), name='login')
]

app/views.py

# Create your views here.

# 自定义 jwt 
# https://django-rest-framework-simplejwt.readthedocs.io/en/latest/customizing_token_claims.html

import datetime
from typing import Any, Dict
from .models import Users
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.authentication import JWTAuthentication


class LoginSerializer(TokenObtainPairSerializer):

    """ 
    自定义令牌 
    """
    @classmethod
    def get_token(cls, user):
        # 调用父类的get_token方法,生成默认的Token
        token = super().get_token(user)
        
        # 将用户的username和mobile信息添加到Token中
        token['username'] = user.username
        token['mobile'] = user.mobile


        '它会调用父类的get_token方法生成默认的Token,然后将用户的username和mobile信息添加到Token中,最后返回包含这些信息的Token。'

        return token
    
    """
    自定义返回格式
    """
    def validate(self, attrs: Dict[str, Any]) -> Dict[str, str]:
        token_ = super().validate(attrs)
        try:
            u = Users.objects.get(username=attrs['username'])
            token = self.get_token(u)
            # print(token.payload)
            # {'token_type': 'refresh', 'exp': 1705047093, 'iat': 1704960693, 'jti': '03c0df3228244636990bf34b91d3c2fe', 'user_id': 1, 'username': 'admin', 'mobile': None}
            expires_at = token.payload['exp']
            mobile = token.payload['mobile']
            username = token.payload['username']
            expires_at_datetime = datetime.datetime.fromtimestamp(expires_at)

            # 这里我就校验了用户名, 没校验密码,如果想完善可以自行完善!
            if u:
                data = {"msg": "登录成功", "当前token": f"{token_}", "当前登录用户": f"{username}", "当前用户电话号码": f"{mobile}",  "过期时间": f"{expires_at_datetime}"}
                return data
        except ObjectDoesNotExist:
            raise serializers.ValidationError("用户不存在")

jwt在线解密token

image.png
image.png

创建路由

jwt_demo/urls.py

from app.views import LoginView
from django.urls import path, re_path, include

urlpatterns = [

    re_path(
            r"^api-auth/", include("rest_framework.urls", namespace="rest_framework")
           ),
    path('api/login/', LoginView.as_view(), name='login'),
    path("api/app/", include("app.urls"))
]

子应用新建路由文件

app/urls.py

from .views import RoleViewSet
from rest_framework.routers import SimpleRouter

router = SimpleRouter()


router.register(r'role', RoleViewSet)

urlpatterns = [

]
urlpatterns += router.urls

添加视图

app/views.py

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from .models import Role


class RoleSerializer(serializers.ModelSerializer):
    class Meta:
        model = Role
        fields = '__all__'




class RoleViewSet(ModelViewSet):
    """
    角色接口
    """
    queryset = Role.objects.all()
    serializer_class = RoleSerializer

添加数据

http://127.0.0.1:8000/api/app/role/
# 打开页面之后登录, 进行添加操作

这里是使用 postman 方便理解

新增

image.png
image.png

这里表示添加成功, 不在headers中携带token, 则不能访问

image.png

查询

image.png

删除

image.png

image.png

修改

对比上面修改了这个字母, 加了一个s

image.png
image.png

JWT 详细配置可参考官网文档

https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html

  • 36
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值