Django REST框架JWT认证自定义非User模型实现方案求助
这个问题我之前帮好几个开发者踩过坑,核心原因很明确:DRF-JWT的默认组件都是为Django自带的User模型设计的,硬依赖了username字段,而你的自定义User模型显然没这个字段(比如用email作为登录标识)。下面是一步步的解决办法,亲测有效:
1. 先确认自定义User模型的基础配置
首先要确保你已经在settings.py里正确指定了自定义User模型,这是所有操作的前提:
AUTH_USER_MODEL = 'your_app.YourCustomUser'
把your_app和YourCustomUser替换成你实际的app名称和模型类名。
2. 重写JSONWebTokenSerializer
默认的JSONWebTokenSerializer会尝试读取username字段来做用户认证,所以我们要创建自己的序列化器,替换成你的自定义模型的登录字段(比如email)。
在你的app目录下新建一个serializers.py(或者专门的jwt_serializers.py),写入以下代码:
from rest_framework_jwt.serializers import JSONWebTokenSerializer from django.contrib.auth import authenticate from rest_framework import serializers from django.utils.translation import ugettext as _ class CustomJWTSerializer(JSONWebTokenSerializer): # 替换成你自定义模型的登录字段,比如email username_field = 'email' def validate(self, attrs): credentials = { self.username_field: attrs.get(self.username_field), 'password': attrs.get('password') } if all(credentials.values()): user = authenticate(**credentials) if user: if not user.is_active: msg = _('User account is disabled.') raise serializers.ValidationError(msg) payload = self.jwt_payload_handler(user) return { 'token': self.jwt_encode_handler(payload), 'user': user } else: msg = _('Unable to log in with provided credentials.') raise serializers.ValidationError(msg) else: msg = _('Must include "{username_field}" and "password".').format( username_field=self.username_field) raise serializers.ValidationError(msg)
这里的关键是把username_field改成你自定义模型中用来登录的字段,比如email,然后在validate方法里用这个字段去做用户认证。
3. 重写jwt_payload_handler
默认的payload处理器会自动把username字段加入到JWT的payload中,所以我们要修改它,移除这个字段,换成你的模型对应的字段。
在你的app目录下新建一个jwt_handlers.py,写入:
from rest_framework_jwt.utils import jwt_payload_handler as default_payload_handler def custom_jwt_payload_handler(user): # 先调用默认处理器生成基础payload payload = default_payload_handler(user) # 移除默认的username字段(如果存在) if 'username' in payload: del payload['username'] # 添加你自定义模型的字段,比如email、user_id等 payload['email'] = user.email payload['user_id'] = user.id # 如果你还有其他需要加入payload的字段,都可以在这里添加 return payload
4. 在settings.py中配置JWT使用自定义组件
最后一步,告诉DRF-JWT使用我们刚才重写的序列化器和处理器:
JWT_AUTH = { # 指定自定义的payload处理器 'JWT_PAYLOAD_HANDLER': 'your_app.jwt_handlers.custom_jwt_payload_handler', # 指定自定义的序列化器 'JWT_SERIALIZER_CLASS': 'your_app.serializers.CustomJWTSerializer', }
记得把your_app替换成你实际的app名称,路径要和你刚才创建的文件对应。
额外提示:如果你用的是djangorestframework-simplejwt
现在很多开发者会用更活跃、维护更好的djangorestframework-simplejwt替代旧的django-rest-framework-jwt,如果是这种情况,解决方法更简洁:
只需要重写TokenObtainPairSerializer即可:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer class CustomTokenObtainPairSerializer(TokenObtainPairSerializer): @classmethod def get_token(cls, user): token = super().get_token(user) # 添加自定义字段到token的payload中 token['email'] = user.email return token def validate(self, attrs): data = super().validate(attrs) # 在返回的响应数据中添加用户信息(可选) data['user_id'] = self.user.id data['email'] = self.user.email return data
然后在settings.py里配置:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 'REFRESH_TOKEN_LIFETIME': timedelta(days=1), # 指定自定义的序列化器 'TOKEN_OBTAIN_SERIALIZER': 'your_app.serializers.CustomTokenObtainPairSerializer', # 其他配置可以根据需求调整 }
内容的提问来源于stack exchange,提问作者Ali

