一、用户登录
1.1 接口设计
1.1.1 用户登录接口设计
POST /users/login/
Body 请求参数
{
"username": "xinlan",
"password": "123456"
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» username | body | string | 是 | 用户名 | 用户名 |
» password | body | string | 是 | 密码 | 密码 |
返回示例
成功
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbiI6InJlZnJlc2giLCJleHAiOjE2MTQ4NTIyODQsImp0aSI6IjkwNWEzOTdiYTQ0ODQ1OTRiZTU1MDI4NmJjNWJiZjIwIiwidXNlcl9pZCI6MX0.WktTD8vABca5nQ0c9RB-KNOsaV-E7_ziQnjezndRzkk",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbiI6ImFjY2VzcyIsImV4cCI6MTYxNDc2NjE4NCwianRpIjoiZDk2MjFmZjhkMWNjNGJhODlkNTExNTQw·YjdlMTQ5ZjQiLCJ1c2VyX2lkIjoxfQ.IcCRz9kxGlwKTfCeScwGsWEjhoxPBbP6_HT3TMwBZHM"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
返回数据结构
状态码 200
名称 | 类型 | 必选 | 约束 | 中文名 | 说明 |
» refresh | string | true | none | 刷新token | none |
» token | string | true | none | token | none |
1.1.2 token刷新接口设计
POST /users/token/refresh/
Body 请求参数
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYzMDkzMzEwMywianRpIjoiNzc3YTg3OWYyZTM4NGExYzhlYjFhYzVhMTA5ZTU4MGQiLCJ1c2VyX2lkIjoxfQ.8duDfoZmUJBjqgyyJQqANKl5zcHpZDN8phuNoGFQBm8"
}
请求参数
名称 | 位置 | 类型 | 必选 | 中文名 | 说明 |
body | body | object | 否 | none | |
» refresh_token | body | string | 是 | 刷新token值 | 刷新token值 |
返回示例
成功
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjMwOTk1NDY3LCJqdGkiOiI2YTIwZmIwZGZmZDA0YTJiYTAwOWM0YjNjNTQ2NjZkOCIsInVzZXJfaWQiOjF9.tdCvibq2d8ts_uCbqAvCVr1JOymdbHw7HLB7QckzbUs"
}
返回结果
状态码 | 状态码含义 | 说明 | 数据模型 |
200 | 成功 | Inline |
返回数据结构
状态码 200
名称 | 类型 | 必选 | 约束 | 中文名 | 说明 |
» token | string | true | none | token | token |
二、重写验证视图
simplejwt
已经实现了验证视图,但我们的项目中需要修改返回数据结构,所以要重写视图。
1. 在 users/ 下创建 serializers.py 模块,编写如下代码:
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenRefreshSerializer
from .models import User
class MyTokenSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
# 修改access为token
data['token'] = data.pop('access')
# 添加额外数据
data['username'] = self.user.username
data['id'] = self.user.id
data['msg'] = '登录成功'
return data
class MyRefreshTokenSerializer(TokenRefreshSerializer):
def validate(self, attrs):
data = super().validate(attrs)
data['token'] = data.pop('access')
return data
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'password', 'mobile', 'is_staff', 'is_superuser']
2. 在 users/views.py 中编写如下视图:
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView as TRV
from .serializers import MyTokenSerializer, MyRefreshTokenSerializer, UserSerializer
from .models import User
class LoginView(TokenObtainPairView):
serializer_class = MyTokenSerializer
class TokenRefreshView(TRV):
serializer_class = MyRefreshTokenSerializer
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
3. 在 users/ 下创建 urls.py 模块,编写如下路由:
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2023/2/8 21:25
# E-mail:117220100@qq.com
# @Author : 心蓝
from django.urls import path
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register('users', views.UserViewSet)
urlpatterns = [
path('users/login/', views.LoginView.as_view(), name='login'),
path('users/token/refresh/', views.TokenRefreshView.as_view(), name='token-refresh'),
]
urlpatterns += router.urls
4、在根路由中包含 users.urls
注意:根路由需要配置path('drf/', include('rest_framework.urls')),
因为,如果不配置这个路由的话,那么在页面中不能进行登出操作
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('drf/', include('rest_framework.urls')),
path('', include('users.urls'))
]
三、simple-jwt 配置
Simple JWT的一些行为可以通过settings.py中的设置变量来定制:
默认设置如下:
from datetime import timedelta
...
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), # token过期时间
'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # 刷新token过期时间
'ROTATE_REFRESH_TOKENS': False, # 设置为true时,每次刷新token时都会更新refreshtoken的过期时间
'BLACKLIST_AFTER_ROTATION': True, # 当设置为True时,会在每次刷新token后把刷新token添加
进黑名单(ROTATE_REFRESH_TOKENS设置为True,并且起用了黑名单)
'UPDATE_LAST_LOGIN': False, # 是否更新最后一次登录时间
'ALGORITHM': 'HS256', # 令牌的签名算法
'SIGNING_KEY': settings.SECRET_KEY, # 签名秘钥
'VERIFYING_KEY': None, # 验证秘钥 如果ALGORITHM设置了哈希密码算法,VERIFYING_KEY会被忽
略,SIGNING_KEY会被使用。如果ALGORITHM设置了一个RSA算法,VERIFYING_KEY必须设置为一个RSA公钥
'AUDIENCE': None, # aud字段声明,设置为None则解码的payload中不包含aud字段
'ISSUER': None, # 发行人 iss字段,设置为None则解码的payload中不包含
'JWK_URL': None, # JWK_URL用于动态解析验证令牌签名所需的公钥。
'LEEWAY': 0, # Leeway是用来给过期时间留出一些空间的。这可以是一个以秒为单位的整数,也可以是
datetime.timedelta。
'AUTH_HEADER_TYPES': ('Bearer',), # 需要身份验证的视图将接受的授权头类型。
'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', # 用于身份验证的授权头名称。HTTP_xxxx 请求头
则为xxxx
'USER_ID_FIELD': 'id', # 用户识别字段在数据中对应的字段
'USER_ID_CLAIM': 'user_id', # 用户识别字段在payload中的字段名,默认user_id
'USER_AUTHENTICATION_RULE':
'rest_framework_simplejwt.authentication.default_user_authentication_rule', # 用户校验
规则
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), # 令牌生
成类
'TOKEN_TYPE_CLAIM': 'token_type', # 令牌类型字段名
'JTI_CLAIM': 'jti', # 令牌标识符字段名
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', # 用于存储滑动令牌刷新周期的过期时间
的声明名称。
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), # 滑动token的有效时间
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), # 滑动刷新token的有效
}
配置说明详见官方文档
我们项目中仅配置两项:
# settings.py
from datetime import timedelta
...
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=1), # 便于开发设置token过期时间1天
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
}
四、鉴权
- 需要鉴权的接口
-
- 除注册,登录接口外,其他接口均需要权限才能访问
- 鉴权类型
-
- JWT
- 鉴权方式
-
- 从登录接口响应数据中获取token,以 Authorization 为请求头的key, Bearer token 作为value。
-
- Bearer 为前缀,与token值之间有一个空格,例如:
Authorization: Bearer
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbiI6ImFjY2VzcyIsImV4cCI6MTYxNDY4ODU2N
SwianRpIjoiYmE4MjFiY2YzODJiNGEyMThiNTlhYWFkZmQ1YjFkYzYiLCJ1c2VyX2lkIjoxfQ.F2EvMh2a
nxtpVRIxwhu1PHbLmryAAvglZ9c1AogDU9s