Django 自定义 JWT 认证类文档(无username模型适配)
Django 自定义 JWT 认证类文档(无username模型适配)
一、概述
CustomJWTAuthentication是一个基于rest_framework_simplejwt的自定义认证类,主要解决以下核心问题:
- 适配移除了username字段的用户模型(如使用mobile/email作为登录标识);
- 增强令牌声明的可观测性(记录令牌包含的所有声明);
- 细化异常处理逻辑(用户不存在、令牌缺失标识等场景);
- 通过日志记录关键认证过程(便于生产环境问题排查)。
二、类定义与核心功能
2.1 类继承关系
class CustomJWTAuthentication(JWTAuthentication):
"""
自定义JWT认证类,适配无username字段的用户模型
"""
继承自JWTAuthentication(SimpleJWT 提供的默认认证类),重写get_user方法以适配无username模型。
2.2 核心方法:get_user
该方法从验证后的令牌中提取用户标识(如 UUID),并通过用户模型的id字段查询用户。以下是关键逻辑解析:
2.2.1 变量初始化与模型获取
user_id = None # 提前定义变量以避免引用前未赋值
User = get_user_model() # 获取项目自定义用户模型(支持动态替换)
2.2.2 令牌声明解析与日志记录
# 记录令牌中的所有声明(关键调试信息)
token_claims = list(validated_token.keys()) # 注意:此处可能存在属性引用问题
logger.debug(f"令牌中包含的声明: {token_claims}")
# 获取配置的用户ID声明字段(从 settings.py 中读取)
user_id_claim = api_settings.USER_ID_CLAIM
logger.debug(f"配置的用户ID声明字段: {user_id_claim}")
user_id = validated_token.get(user_id_claim) # 提取用户ID值
注意:潜在问题
validated_token.keys()可能引发Unresolved attribute reference "keys" for class 'Token'警告。
原因:validated_token是Token类实例(如AccessToken),其内部通过payload属性存储令牌内容。部分 IDE(如 PyCharm)可能无法正确识别Token类的keys()方法(实际Token类实现了字典接口,支持keys())。
替代方案(更明确):
token_payload = validated_token.payload # 获取令牌负载(字典类型)
token_claims = list(token_payload.keys()) # 从负载中获取声明
2.2.3 用户ID缺失处理
if user_id is None:
# 尝试其他常见用户ID声明名称(兼容不同令牌生成方式)
possible_claims = ['user_id', 'sub', 'uid', 'id']
for claim in possible_claims:
value = validated_token.get(claim)
if value:
logger.warning(f"发现可能的用户标识声明 '{claim}': {value}") # 提示潜在替代声明
logger.warning("令牌中缺少用户标识声明")
raise InvalidToken(_('Token contains no user identifier')) # 抛出无效令牌异常
2.2.4 用户查询与异常处理
try:
logger.debug(f"尝试认证用户: user_id={user_id}") # 记录认证尝试
return User.objects.get(id=user_id) # 通过 UUID 主键查询用户
except User.DoesNotExist:
# 确保 user_id 已定义(避免空值导致日志混乱)
user_id_str = str(user_id) if user_id is not None else "未知"
logger.warning(f"用户不存在: user_id={user_id_str}") # 警告日志(用户不存在)
raise AuthenticationFailed(_('User not found')) # 抛出认证失败异常
except Exception as e:
# 通用异常处理(如数据库错误)
user_id_str = str(user_id) if user_id is not None else "未知"
logger.error(f"用户认证失败 (user_id={user_id_str}): {str(e)}", exc_info=True) # 错误日志(含堆栈)
raise InvalidToken(_('Authentication failed: ') + str(e)) # 抛出无效令牌异常
三、关键改进点
3.1 增强令牌声明可观测性
- 记录令牌中包含的所有声明(如['user_id', 'exp', 'iat']),便于调试时确认令牌内容是否符合预期;
- 当配置的USER_ID_CLAIM缺失时,自动尝试其他常见声明(如sub、uid),提高兼容性。
- 用户不存在:单独捕获User.DoesNotExist异常,记录具体user_id并抛出明确错误;
- 通用异常:捕获所有其他异常(如数据库连接失败),记录完整堆栈信息,避免认证流程静默失败。
- DEBUG 级别:记录令牌声明、配置的用户ID字段等调试信息;
- WARNING 级别:提示潜在的用户ID声明、用户不存在等边界情况;
- ERROR 级别:记录认证失败的详细原因(含异常堆栈),便于快速定位代码问题。
3.2 细化异常处理逻辑
3.3 安全日志记录
四、使用示例
4.1 配置settings.py
在 Django 项目配置中指定用户模型和 SimpleJWT 参数:
# settings.py
AUTH_USER_MODEL = 'users.User' # 自定义用户模型路径
SIMPLE_JWT = {
'USER_ID_FIELD': 'id', # 用户模型的唯一标识字段(UUID 主键)
'USER_ID_CLAIM': 'user_id', # JWT 中存储用户ID的声明字段
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30), # 令牌生命周期(可选)
}
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'apps.users.auth.authentication.CustomJWTAuthentication', # 注册自定义认证类
],
}
4.2 在视图中使用认证类
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from apps.users.auth.authentication import CustomJWTAuthentication
class SecureAPIView(APIView):
authentication_classes = [CustomJWTAuthentication] # 指定认证类
permission_classes = [IsAuthenticated] # 仅允许认证用户访问
def get(self, request):
# 已认证用户通过 request.user 访问
return Response({
"message": "认证成功",
"user_id": str(request.user.id),
"mobile": request.user.mobile
})
五、已知问题与修复建议
5.1validated_token.keys()的 IDE 警告
- 问题现象:IDE(如 PyCharm)提示Unresolved attribute reference "keys" for class 'Token';
- 原因:Token类的keys()方法通过字典接口动态实现,部分 IDE 无法正确识别;
- 修复建议(可选):显式访问payload属性的keys()方法(更明确):token_payload = validated_token.payload # 获取令牌负载(字典类型)
token_claims = list(token_payload.keys()) # 从负载中获取声明
5.2 令牌声明兼容性优化
- 若项目中使用的令牌生成方(如第三方认证服务)使用非标准声明(如uid),可扩展possible_claims列表:possible_claims = ['user_id', 'sub', 'uid', 'id', 'custom_user_id'] # 添加自定义声明
六、总结
CustomJWTAuthentication类通过增强令牌声明解析、细化异常处理和安全日志记录,完美适配无username字段的用户模型。通过本类,可在生产环境中快速排查认证问题(如令牌缺失用户ID、用户不存在),并确保认证流程的稳定性和可观测性。
浙公网安备 33010602011771号