eagleye

Django REST Framework 令牌验证端点开发与测试指南

Django REST Framework 令牌验证端点开发与测试指南

一、概述

本指南适用于在 Django REST Framework (DRF) 中开发并测试一个JWT 令牌验证端点(如/api/users/token/validate/),用于验证访问令牌(Access Token)的有效性。通过本指南,您将掌握以下核心内容:

  • 如何从请求中提取并解析 JWT 令牌;
  • 如何验证令牌的签名、有效期、受众(Audience)等关键属性;
  • 使用 Apipost 工具测试该端点的完整流程;
  • 常见验证失败场景的排查方法。

二、请求处理流程与数据提取

2.1 请求处理核心逻辑

Apipost 发送一个包含 JSON Body 的 POST 请求时,DRF 的请求处理器会自动解析application/json格式的数据,并将解析结果存储在request.data字典中。具体流程如下:

graph TD

A[Apipost 发送 POST 请求] --> B{Content-Type 检查}

B -->|application/json| C[DRF 解析 JSON 数据]

C --> D[数据存入 request.data 字典]

D --> E[视图函数通过 request.data 获取字段]

2.2 从 request.data 中提取令牌

在视图函数中,通过request.data.get('token')可以获取请求体中的token字段值(类型为字符串)。示例:

def post(self, request):

# 获取请求体中的 "token" 字段

token_str = request.data.get('token') # 值为 "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

三、令牌验证视图开发(完整代码示例)

3.1 视图功能说明

该视图支持以下功能:

  • 从请求体中提取令牌;
  • 去除Bearer前缀(可选);
  • 验证令牌的签名、有效期、受众(Audience)和签发者(Issuer);
  • 返回验证结果(有效/无效)及详细信息(如用户 ID、过期时间)。

3.2 完整视图代码

from rest_framework.views import APIView

from rest_framework.response import Response

from rest_framework import status

from rest_framework_simplejwt.tokens import AccessToken

from rest_framework_simplejwt.exceptions import TokenError

import time # 用于计算剩余有效期

class TokenValidationView(APIView):

permission_classes = [] # 允许未认证用户访问(仅用于验证端点)

def post(self, request):

# 1. 从请求体中获取 "token" 字段

token_str = request.data.get('token')

if not token_str:

return Response(

{"error": "需要提供 'token' 参数"},

status=status.HTTP_400_BAD_REQUEST

)

# 2. 去除 "Bearer " 前缀(可选,根据前端传递方式决定)

if token_str.startswith("Bearer "):

pure_token = token_str[7:] # 去掉前7个字符("Bearer ")

else:

pure_token = token_str

# 3. 验证令牌

try:

token = AccessToken(pure_token) # 初始化令牌对象(自动验证签名)

token.verify() # 显式验证(可选,初始化时已默认验证)

# 计算剩余有效期(秒)

current_time = int(time.time())

remaining_seconds = token.payload['exp'] - current_time

return Response({

"status": "valid",

"payload": token.payload, # 令牌负载(包含用户信息)

"user_id": token.payload.get('user_id'), # 用户唯一标识

"audience": token.payload.get('aud'), # 受众(如 "web-app")

"issuer": token.payload.get('iss'), # 签发者(如 "your-service")

"exp": token.payload.get('exp'), # 过期时间戳(Unix 时间)

"exp_date": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(token.payload['exp'])), # 可读时间格式

"remaining_seconds": remaining_seconds # 剩余有效时间(秒)

})

except TokenError as e:

# 处理令牌验证失败(如过期、签名错误)

return Response({

"status": "invalid",

"error": str(e), # 错误详情(如 "Token has expired")

"received_token": token_str # 返回原始令牌(仅调试使用)

}, status=status.HTTP_400_BAD_REQUEST)

四、使用 Apipost 测试验证端点

4.1 测试准备

  • 确保 Django 服务已启动(python manage.py runserver);
  • 获取一个有效的 JWT 访问令牌(通过登录接口/api/users/login/获取)。

4.2 测试步骤

步骤 1:配置请求基本信息

配置项

说明

请求方法

POST

URL

http://localhost:8000/api/users/token/validate/(根据实际路由调整)

请求头

Content-Type: application/json(必须,否则 DRF 无法解析 JSON)

步骤 2:设置请求体(JSON)

Apipost 的Body标签页选择raw格式,输入以下内容(替换eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...为实际令牌):

{

"token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

}

步骤 3:发送请求并验证响应

  • 成功响应(令牌有效)

状态码200 OK,响应体示例:

{

"status": "valid",

"payload": {

"token_type": "access",

"exp": 1751393603,

"iat": 1751389703,

"jti": "a1b2c3d4-5678-90ef-ghij-klmnopqrstuv",

"user_id": "7c8417bb-9ba3-4ec0-866b-ca9324848693",

"aud": "web-app",

"iss": "safe-sentry-auth-service"

},

"user_id": "7c8417bb-9ba3-4ec0-866b-ca9324848693",

"audience": "web-app",

"issuer": "safe-sentry-auth-service",

"exp": 1751393603,

"exp_date": "2025-07-02 02:13:23",

"remaining_seconds": 13465

}

  • 失败响应(令牌无效)

状态码400 Bad Request,响应体示例(令牌过期):

{

"status": "invalid",

"error": "Token has expired",

"received_token": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

}

五、常见问题排查

5.1 令牌验证失败的常见原因及解决方法

错误信息

可能原因

解决方法

"Invalid token"

令牌格式错误(如非 JWT 字符串)或签名不匹配(令牌被篡改)

检查令牌是否完整(无截断);确保服务器使用的签名密钥与生成令牌时一致

"Token has expired"

令牌已超过exp字段指定的过期时间

使用刷新令牌(Refresh Token)调用/api/users/token/refresh/获取新令牌

"Token has wrong audience"

令牌的aud字段与服务器预期的受众(如"web-app")不匹配

检查令牌生成逻辑,确保aud字段与服务器配置一致

"Token has invalid issuer"

令牌的iss字段与服务器预期的签发者(如"your-service")不匹配

检查令牌生成逻辑,确保iss字段与服务器配置一致

5.2 请求体未解析(request.data为空)

  • 可能原因:请求头未设置Content-Type: application/json。
  • 解决方法:在 Apipost 的请求头中添加Content-Type: application/json(注意大小写和空格)。
  • 可能原因:请求体中token字段名称错误(如写成Token或access_token)。
  • 解决方法:确保请求体中字段名称为token(与视图中request.data.get('token')一致)。
  • 移除调试信息:生产环境中应删除received_token字段(避免泄露原始令牌);
  • 速率限制:使用django-ratelimit限制该端点的请求频率(如每分钟 10 次),防止暴力破解;
  • 身份验证:对于敏感操作(如验证管理令牌),可要求用户先通过认证(移除permission_classes = [])。

5.3token_str为None(未获取到令牌)

六、注意事项

6.1 生产环境安全增强

6.2 令牌前缀处理

若前端传递的令牌不带Bearer前缀(如直接传递"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."),需删除视图中的前缀处理逻辑:

# 无需处理前缀

pure_token = token_str

6.3 时间戳转换

exp_date字段通过time.strftime转换为可读时间,但需注意时区问题(默认使用服务器本地时区)。如需 UTC 时间,可使用time.gmtime():

"exp_date": time.strftime("%Y-%m-%d %H:%M:%S UTC", time.gmtime(token.payload['exp']))

通过本指南,您可以快速开发并测试一个健壮的 JWT 令牌验证端点,有效解决/api/users/me/等需要认证的接口因令牌问题导致的403 Forbidden错误,提升 API 的安全性和可靠性。

 

posted on 2025-07-02 10:48  GoGrid  阅读(46)  评论(0)    收藏  举报

导航