You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 15:55:06