在Django REST Framework中自定义用户Token生成实现求助
问题描述
我正在开发一个应用,已编写API用于通过手机号验证创建用户。该POST请求格式如下:
{ "phone_number": "", "username": "", "bio": "", "profilePic": null }
当模型序列化器验证通过后,需返回包含Token的用户信息(供后续请求使用),格式如下:
{ "phone_number": "", "username": "", "bio": "", "profilePic": null, "token": "28duw8he282hd2sszajqj" }
我需要创建自定义认证类,现寻求技术帮助。以下是我的用户模型类:
class WhIronUsers(models.Model): phone_number = models.CharField(max_length=100, verbose_name="phone_number", unique=True) username = models.CharField(max_length=100, verbose_name="username") bio = models.CharField(max_length=255, verbose_name="bio") profilePic = models.ImageField(upload_to="images/profile")
以及需要生成Token的视图类:
from rest_framework import generics from .serializers import WhIronUserSerializer from .models import WhIronUsers class WhIronUserView(generics.ListCreateAPIView): serializer_class = WhIronUserSerializer queryset = WhIronUsers.objects.all() class WhIronDetailUserView(generics.RetrieveDestroyAPIView): serializer_class = WhIronUserSerializer queryset = WhIronUsers.objects.all()
解决方案
1. 配置DRF Token认证
首先在项目settings.py中启用DRF的Token认证模块,并设置默认认证方式:
INSTALLED_APPS = [ # ... 已有应用 'rest_framework', 'rest_framework.authtoken', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.SessionAuthentication', # 保留方便调试 ], }
执行迁移生成Token表:
python manage.py migrate
2. 优化用户模型(适配认证体系)
原用户模型缺少认证相关字段,继承AbstractBaseUser来适配Django认证系统,用手机号作为登录标识:
from django.db import models from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin class WhIronUserManager(BaseUserManager): def create_user(self, phone_number, username, bio, password=None, **extra_fields): if not phone_number: raise ValueError('必须填写手机号') user = self.model( phone_number=phone_number, username=username, bio=bio, **extra_fields ) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, phone_number, username, bio, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self.create_user(phone_number, username, bio, password, **extra_fields) class WhIronUsers(AbstractBaseUser, PermissionsMixin): phone_number = models.CharField(max_length=100, verbose_name="手机号", unique=True) username = models.CharField(max_length=100, verbose_name="用户名") bio = models.CharField(max_length=255, verbose_name="个人简介") profilePic = models.ImageField(upload_to="images/profile", null=True, blank=True) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) objects = WhIronUserManager() USERNAME_FIELD = 'phone_number' # 设置手机号为登录唯一标识 REQUIRED_FIELDS = ['username', 'bio']
执行迁移更新用户表:
python manage.py makemigrations python manage.py migrate
3. 修改序列化器,生成并返回Token
修改WhIronUserSerializer,在用户创建时自动生成Token并包含在响应中:
from rest_framework import serializers from .models import WhIronUsers from rest_framework.authtoken.models import Token class WhIronUserSerializer(serializers.ModelSerializer): token = serializers.CharField(read_only=True) # 只读字段,返回Token class Meta: model = WhIronUsers fields = ['phone_number', 'username', 'bio', 'profilePic', 'token'] extra_kwargs = { 'profilePic': {'required': False} # 允许注册时不传头像 } def create(self, validated_data): # 创建用户,不需要密码可删除password参数,改用objects.create() user = WhIronUsers.objects.create_user( phone_number=validated_data['phone_number'], username=validated_data['username'], bio=validated_data['bio'], profilePic=validated_data.get('profilePic', None), password=validated_data.get('password') ) # 生成或获取用户Token token, created = Token.objects.get_or_create(user=user) user.token = token.key return user
4. 自定义手机号认证类
如果需要支持用户通过手机号+密码获取Token,创建自定义认证类:
from rest_framework import authentication from rest_framework.exceptions import AuthenticationFailed from .models import WhIronUsers class PhoneNumberAuthentication(authentication.BaseAuthentication): def authenticate(self, request): phone_number = request.data.get('phone_number') password = request.data.get('password') if not phone_number or not password: return None try: user = WhIronUsers.objects.get(phone_number=phone_number) except WhIronUsers.DoesNotExist: raise AuthenticationFailed('用户不存在') if not user.check_password(password): raise AuthenticationFailed('密码错误') return (user, None)
配套创建获取Token的视图:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authtoken.models import Token from .authentication import PhoneNumberAuthentication class ObtainTokenView(APIView): authentication_classes = [PhoneNumberAuthentication] def post(self, request): token, created = Token.objects.get_or_create(user=request.user) profile_pic_url = request.user.profilePic.url if request.user.profilePic else None return Response({ 'phone_number': request.user.phone_number, 'username': request.user.username, 'bio': request.user.bio, 'profilePic': profile_pic_url, 'token': token.key })
5. 优化视图权限控制
给原视图添加权限控制,避免匿名用户访问敏感接口:
from rest_framework import generics, permissions from .serializers import WhIronUserSerializer from .models import WhIronUsers class WhIronUserView(generics.ListCreateAPIView): serializer_class = WhIronUserSerializer queryset = WhIronUsers.objects.all() def get_permissions(self): if self.request.method == 'POST': # 注册接口允许匿名访问 return [permissions.AllowAny()] # 用户列表仅允许认证用户访问 return [permissions.IsAuthenticated()] class WhIronDetailUserView(generics.RetrieveDestroyAPIView): serializer_class = WhIronUserSerializer queryset = WhIronUsers.objects.all() permission_classes = [permissions.IsAuthenticated()] # 仅认证用户可访问详情和删除
内容的提问来源于stack exchange,提问作者Amirhosein Pourmoshir
相关产品推荐
相关产品推荐

