Django之JWT库与SimpleJWT库的使用 您所在的位置:网站首页 python生成jwt Django之JWT库与SimpleJWT库的使用

Django之JWT库与SimpleJWT库的使用

2024-04-29 01:03| 来源: 网络整理| 查看: 265

JWT JWT概述

JWT(JSON Web Token)是一种轻量级的认证和授权机制,它是基于 JSON 格式的标准,用于在网络应用程序或服务之间传递声明。

JWT官网:https://jwt.io/

特点:

无状态:JWT 在服务器端不保存任何信息,因此可以跨多个请求进行身份验证。 自包含:JWT 包含了所有必要的信息,这样不需要去查询数据库或其他存储设施来验证用户身份。 可扩展性:由于 JWT 是基于 JSON 格式的标准,因此可以很容易地扩展以添加自定义数据。 强安全性:JWT 使用数字签名来验证消息的完整性和真实性,这样可以确保消息没有被篡改。 跨域支持:由于 JWT 是通过 HTTP 头部传输的,因此可以轻松地在跨域场景中使用。

主要应用:

JWT通常用于身份验证和授权。当用户成功登录时,服务器会生成一个 JWT,并将其返回给客户端。客户端随后将该令牌放入每个后续请求的Authorization标头中,以证明其已通过身份验证。服务器使用私钥来验证签名,并从 JWT 中提取必要的信息,例如用户 ID 和权限。

JWT组成:

JWT由三部分组成:头部、载荷和签名。

1.头部包含关于生成 JWT的算法和类型的元数据。 2.载荷是JWT的主体内容,它包含有关用户或其他实体的信息,例如其 ID 或角色。 3.签名是使用密钥对头部和载荷进行加密的字符串,以确保JWT 在传输过程中不被篡改。

一个JWT Token字符串,由3部分组成,用.隔开

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c 头部(header)

JWT的头部承载两部分信息:

声明类型,这里是jwt 声明加密的算法 通常直接使用HMAC SHA256 { "alg": "HS256", "typ": "JWT" }

将头部进行base64加密构成了第一部分

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 载荷(payload)

载荷就是存放有效信息的地方。有效信息包含三个部分

1.标准中注册的声明(建议但不强制使用) :

iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: jwt的过期时间,这个过期时间必须要大于签发时间 nbf: 定义在什么时间之前,该jwt都是不可用的 iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

2.公共的声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。但不建议添加敏感信息,因为该部分在客户端可解密

3.私有的声明

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息

{ "sub": "1234567890", "name": "John Doe", "iat": 1656239122 }

将其进行base64加密,得到JWT的第二部分

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ 签名(signature)

JWT的第三部分是一个签名信息,用来防止JWT token被伪造。

签名生成过程:

1.服务器在生成jwt token时,会将header和payload字符串进行拼接,用.隔开 2.使用一个只有服务器知道的密钥对拼接后的内容进行加密,加密之后生成的字符串就是signature内容

签名验证过程:

1.将客户端传递的jwt token中的header和payload字符串进行拼接,用.隔开 2.使用服务器自己的密钥对拼接之后的字符串进行加密 3.将加密之后的内容和将客户端传递的jwt token中signature进行对比,如果不一致,就说明jwt token是被伪造的 HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), 256-bit-secret )

注意:

不要在jwt的payload部分存放敏感信息,客户端可解密得到 保护好secret密钥,使用https协议

image.png

Django使用JWT说明

在Python项目中使用JWT生成和校验Token,可以使用django-rest-framework-jwt或djangorestframework-simplejwt扩展来完成。

django-rest-framework-jwt

GitHub地址:https://github.com/jpadilla/django-rest-framework-jwt

文档:https://jpadilla.github.io/django-rest-framework-jwt/

djangorestframework-simplejwt

GitHub地址:https://github.com/jazzband/djangorestframework-simplejwt 文档:https://django-rest-framework-simplejwt.readthedocs.io/en/latest/

注意:django-rest-framework-jwt不再维护,且适合低版本框架使用,若使用最新版本的Django和DRF如果使用JSON Web Token,项目启动会报错

ImportError: Could not import 'rest_framework_jwt.authentication.JSONWebTokenAuthentication' for API setting 'DEFAULT_AUTHENTICATION_CLASSES'. ImportError: cannot import name 'smart_text' from 'django.utils.encoding' jwt库的使用 安装依赖库

使用pip命令安装djangorestframework-jwt库

pip install djangorestframework-jwt 配置settings.py文件

添加以下内容到INSTALLED_APPS和REST_FRAMEWORK配置中

INSTALLED_APPS = [ ... 'rest_framework', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( # 引入JWT认证机制,当客户端将jwt token传递给服务器之后 # 此认证机制会自动校验jwt token的有效性,无效会直接返回401(未认证错误) 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), } # JWT扩展配置 JWT_AUTH = { # 设置生成jwt token的有效时间 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1), }

其他可选配置项:

'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()) 配置urls.py文件

配置urls.py文件,添加以下路由配置

from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token urlpatterns = [ path('api-token-auth/', obtain_jwt_token), path('api-token-refresh/', refresh_jwt_token), path('api-token-verify/', verify_jwt_token), ] 创建视图

创建自定义视图以生成JWT令牌

JWT扩展的提供了生成JWT Token的方法:

from rest_framework_jwt.settings import api_settings data = { "name": "Django", "name": "20"} payload = api_settings.JWT_PAYLOAD_HANDLER(user) jwt_token = api_settings.JWT_ENCODE_HANDLER(payload) from rest_framework_jwt.settings import api_settings # 继承JSONWebTokenAPIView,使用CustomJWTSerializer进行序列化和验证 class CustomObtainJSONWebToken(JSONWebTokenAPIView): serializer_class = CustomJWTSerializer class CustomJWTSerializer(JSONWebTokenSerializer): # validate 方法对提交的用户凭据进行验证,如果验证通过则生成 JWT token 并返回给客户端 def validate(self, attrs): # 从请求中获取用户名和密码 credentials = { self.username_field: attrs.get(self.username_field), 'password': attrs.get('password') } # 通过 Django 自带的 authenticate 函数进行认证 if all(credentials.values()): user = authenticate(**credentials) if user: # 检查该用户是否处于活跃状态,如果被禁用则返回错误信息 if not user.is_active: raise serializers.ValidationError('User account is disabled.') # 生成JWT的payload(负载) payload = api_settings.JWT_PAYLOAD_HANDLER(user) # 生成JWT token jwt_token = api_settings.JWT_ENCODE_HANDLER(payload) response_data = { 'token': jwt_token, } # 将生成的 JWT token 返回给客户端 return response_data else: raise serializers.ValidationError('Unable to log in with provided credentials.') else: raise serializers.ValidationError('Must include "{username_field}" and "password".'.format(username_field=self.username_field)) 配置权限

在视图中使用 @permission_classes 装饰器指定需要验证的权限。

from rest_framework.permissions import IsAuthenticated class CustomView(APIView): permission_classes = (IsAuthenticated,) SimpleJWT库的使用 安装SimpleJWT库

通过pip命令安装SimpleJWT库:

pip install djangorestframework-simplejwt 配置Django项目

在settings.py文件中添加以下配置:

INSTALLED_APPS = [ 'rest_framework', 'rest_framework_simplejwt', ] REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( # 将全局权限控制方案设置为仅允许认证用户访问 'rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_AUTHENTICATION_CLASSES': ( # JWT认证:使用SimpleJWT库提供的JWTAuthentication类来进行认证 'rest_framework_simplejwt.authentication.JWTAuthentication', # sesssion认证 'rest_framework.authentication.SessionAuthentication', # 基本认证 'rest_framework.authentication.BasicAuthentication', ), } SIMPLE_JWT = { # token有效时长(返回的 access 有效时长) 'ACCESS_TOKEN_LIFETIME': datetime.timedelta(seconds=30), # token刷新的有效时间(返回的 refresh 有效时长) 'REFRESH_TOKEN_LIFETIME': datetime.timedelta(seconds=20), }

更多Simple JWT的配置参考: https://django-rest-framework-simplejwt.readthedocs.io/en/latest/settings.html

配置路由

配置项目级路由

from django.urls import path, include, re_path urlpatterns = [ path('admin/', include(('apps.admin.urls', 'admin'), namespace="admin")), ]

配置子应用JWT视图的路由

from django.urls import include, path from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView urlpatterns = [ path('login/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('verify/', TokenVerifyView.as_view(), name='token_verify'), ]

上面视图路由使用的是使用JWT自身的类,也可以自定义视图实现相关功能

from rest_framework.views import APIView from rest_framework.response import Response from rest_framework_simplejwt.tokens import AccessToken, RefreshToken from rest_framework.permissions import IsAuthenticated class TokenObtainView(APIView): # 视图需要被认证才能访问 permission_classes = [IsAuthenticated] def post(self, request): # 使用了SimpleJWT提供的AccessToken和RefreshToken类,为认证成功的用户生成对应的JWT令牌 access_token = AccessToken.for_user(request.user) refresh_token = RefreshToken.for_user(request.user) return Response({ 'access_token': str(access_token), 'refresh_token': str(refresh_token), }) from django.urls import path from .views import TokenObtainView urlpatterns = [ path('api/token/', TokenObtainView.as_view(), name='token_obtain'), ] 创建用户

使用Django自带用户认证系统,创建一个用户,用于登录

import os from django.test import TestCase from django.contrib.auth.models import User if not os.environ.get('DJANGO_SETTINGS_MODULE'): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'meiduo_mall.settings') import django django.setup() class MyTest(TestCase): User.objects.create_user(username='admin', password='admin') 接口测试

1.访问loginimage.png

2.登录image.png

登录接口,返回refresh和access值

refresh用来刷新获取新的Token值 access用来请求身份认证的Token access的过期时间参照配置ACCESS_TOKEN_LIFETIME refresh的过期时间参照配置REFRESH_TOKEN_LIFETIME

当用refresh刷新后,access的过期时间等于:当前刷新的此刻时间+ACCESS_TOKEN_LIFETIME

3.Token校验使用登录接口返回的access值,调用Token校验接口image.png

4.刷新Token

使用登录接口返回的refresh值,调用Token刷新接口

当此短期访问令牌过期时,可以使用长期存在的刷新令牌来获取另一个访问令牌:image.png

身份认证

使用返回的访问令牌来证明受保护视图的身份验证

path('test/', TestView.as_view(), name='test'), from rest_framework.response import Response from rest_framework.views import APIView class TestView(APIView): def get(self, request): return Response("tet successfully")

image.png

image.png

在请求头加上Authorization,格式:Bearer [token值] 有空格image.png

自定义令牌声明

自定义令牌需要视图创建一个子类,并为其相应的序列化程序创建一个子类

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from rest_framework_simplejwt.views import TokenObtainPairView class MyTokenObtainPairSerializer(TokenObtainPairSerializer): def validate(self, attrs): data = super().validate(attrs) refresh = self.get_token(self.user) data['refresh'] = str(refresh) data['access'] = str(refresh.access_token) data['username'] = self.user.username return data class MyTokenObtainPairView(TokenObtainPairView): serializer_class = MyTokenObtainPairSerializer

配置指向子类视图的url路由

re_path(r'^login/$', views.MyTokenObtainPairView.as_view()),

image.png

手动创建令牌

为用户手动创建令牌,例如用户注册,注册完后直接返回token

class MyCreateTokenView(APIView): permission_classes = [permissions.AllowAny] def get(self, request, *args, **kwargs): return Response("Get method is not supported !") def post(self, request, *args, **kwargs): refresh = RefreshToken.for_user(request.user) content = { 'refresh': str(refresh), 'access': str(refresh.access_token), } return Response(content) re_path(r'^register/$', views.MyCreateTokenView.as_view()),

image.png

SimpleJWT的应用 实现用户身份验证和JWT token的生成 from django.contrib.auth.models import User from django.utils import timezone from rest_framework import serializers from rest_framework_simplejwt.serializers import TokenObtainPairSerializer from rest_framework_simplejwt.tokens import RefreshToken class AuthSerializer(TokenObtainPairSerializer): id = serializers.IntegerField(label='用户ID', read_only=True) username = serializers.CharField(label='用户名') token = serializers.CharField(label='Token', read_only=True) refresh = serializers.CharField(label='Refresh', read_only=True) # 从attrs参数中获取到传入的用户名和密码 def validate(self, attrs): # 获取username和password username = attrs['username'] password = attrs['password'] # 进行用户名和密码校验 try: user = User.objects.get(username=username) except User.DoesNotExist: raise serializers.ValidationError('用户名或密码错误.') else: # 校验密码 if not user.check_password(password): raise serializers.ValidationError('用户名或密码错误') # 给attrs中添加user属性,保存登录用户 attrs['user'] = user return attrs def create(self, validated_data): # 获取登录用户user user = validated_data['user'] # 设置最新登录时间 user.last_login = timezone.now() user.save() refresh = RefreshToken.for_user(user) # 给user对象增加属性,保存jwt token的数据 user.refresh = str(refresh) user.token = str(refresh.access_token) return user 登录实现

创建了AuthSerializer对象,并将POST请求的参数传入进行校验。如果校验通过,则调用serializer.save()方法生成JWT token,并将token数据作为响应返回。

from rest_framework import status from rest_framework.generics import CreateAPIView from rest_framework.permissions import AllowAny from rest_framework.response import Response from rest_framework.views import APIView from apps.zd_admin.serializers.user import AuthSerializer class LoginView1(APIView): permission_classes = [AllowAny] def post(self, request): # 获取参数并进行校验 serializer = AuthSerializer(data=request.data) serializer.is_valid(raise_exception=True) # 调用序列化器类的create方法,实现服务器签发jwt token serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) class LoginView(CreateAPIView): permission_classes = [AllowAny] serializer_class = AuthSerializer urlpatterns = [ re_path(r'^login/$', views.LoginView.as_view()), ]

image.png

注册实现

注册接口进行注册返回token认证信息也是同理

re_path(r'^register/$', views.RegisterView.as_view()), class RegisterView(APIView): permission_classes = [AllowAny] def post(self, request): user = request.data username = user['username'] password = user['password'] # 保存注册数据 try: user = User.objects.create_user(username=username, password=password) except DatabaseError: raise serializers.ValidationError('注册失败') refresh = RefreshToken.for_user(user) user = { "username": user.username, "token": str(refresh.access_token), "refresh": str(refresh)} return JsonResponse(user)

image.png

Token验证

使用登录、注册得到的Token随着请求携带进行测试

re_path(r'^test/$', views.TestView.as_view()), class TestView(APIView): def get(self, request): return Response("tet successfully")

image.png



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有