Django REST Framework: 构建带有自定义认证的API

在本文中,我们将探讨如何使用Django和Django REST Framework (DRF)构建一个带有自定义认证的API。我们将涵盖从项目设置到处理常见陷阱的整个过程,包括自定义用户模型、序列化器、视图函数、URL配置以及必要的设置。

1. 项目设置

首先,在PyCharm中创建一个新的Django项目(不挂魔法可能会卡死)。然后,使用以下命令创建一个用户管理app:

python manage.py startapp account

2. 模型设计

account/models.py中,创建一个自定义用户模型:

from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    is_admin = models.BooleanField(default=False)

创建这个自定义用户模型后,确保在 settings.py 中设置 AUTH_USER_MODEL(我们将在后面的设置部分详细说明)。

3. 序列化器

account/serializers.py中,创建一个用户序列化器:

from rest_framework import serializers
from .models import User

class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'password', 'is_admin']

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

4. 自定义权限装饰器

account/decorators.py中,创建一个管理员权限装饰器:

from functools import wraps
from django.http import JsonResponse

def admin_required(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.user.is_authenticated and request.user.is_admin:
            return view_func(request, *args, **kwargs)
        else:
            return JsonResponse({'error': 'Admin privileges required'}, status=403)
    return wrapper

5. 自定义认证类

account/authentication.py中,创建一个禁用CSRF检查的认证类(具体原因请看’注意事项和常见陷阱’第六点):

from rest_framework.authentication import SessionAuthentication

class CsrfExemptSessionAuthentication(SessionAuthentication):
    def enforce_csrf(self, request):
        return  # 禁用CSRF检查

6. 视图

account/views.py中,创建视图函数:

from django.contrib.auth import authenticate, login
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.response import Response
from rest_framework import status
from .models import User
from .serializers import UserSerializer
from .decorators import admin_required
from .authentication import CsrfExemptSessionAuthentication
from rest_framework.permissions import IsAuthenticated, AllowAny

@api_view(['GET'])
@authentication_classes([CsrfExemptSessionAuthentication])
@permission_classes([IsAuthenticated])
@admin_required
def user_list(request):
    users = User.objects.all()
    serializer = UserSerializer(users, many=True)
    return Response(serializer.data)

@api_view(['POST'])
@permission_classes([AllowAny])
def register(request):
    serializer = UserSerializer(data=request.data)
    if serializer.is_valid():
        user = serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
@permission_classes([AllowAny])
def login_view(request):
    username = request.data.get('username')
    password = request.data.get('password')
    user = authenticate(request, username=username, password=password)
    if user is not None:
        login(request, user)
        serializer = UserSerializer(user)
        return Response(serializer.data)
    return Response({'error': 'Invalid credentials'}, status=status.HTTP_401_UNAUTHORIZED)

7. URL配置

account/urls.py中,配置URL:

from django.urls import path
from . import views

urlpatterns = [
    path('users/', views.user_list, name='user-list'),
    path('register/', views.register, name='register'),
    path('login/', views.login_view, name='login'),
]

在主项目的urls.py中,包含account app的URLs:

from django.urls import path, include

urlpatterns = [
    path('api/account/', include('account.urls')),
]

8. 设置文件配置

settings.py中,进行必要的配置:

INSTALLED_APPS = [
    # ... 其他应用 ...
    'rest_framework',
    'corsheaders',
    'account',
]

AUTH_USER_MODEL = 'account.User'

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'account.authentication.CsrfExemptSessionAuthentication',
    ],
}

# CORS设置
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",  # 前端开发服务器地址
    "http://127.0.0.1:3000",
]

CORS_ALLOW_CREDENTIALS = True

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'your-backend-ip']

AUTH_USER_MODEL = 'accounts.User'

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # 确保这是第一个中间件
    # ... 其他中间件 ...
    # 'django.middleware.csrf.CsrfViewMiddleware',  # 注释掉这行
]

注意事项和常见陷阱

  1. CORS设置: 当使用session认证时,不能将CORS设置为允许所有源(CORS_ALLOW_ALL_ORIGINS = True)。必须明确指定允许的源。

  2. ALLOWED_HOSTS: 确保将后端服务器的IP地址添加到ALLOWED_HOSTS列表中。

  3. 中间件顺序: corsheaders.middleware.CorsMiddleware应该是中间件列表中的第一项,以确保它能够正确处理CORS头。

  4. AUTH_USER_MODEL 设置: 当使用自定义用户模型时,必须在 settings.py 中设置 AUTH_USER_MODEL = 'account.User'。这个设置告诉 Django 使用您的自定义用户模型,而不是默认的用户模型。重要的是要在项目的早期就设置这个,最好是在进行首次数据库迁移之前。如果在项目后期更改这个设置,可能会导致复杂的数据库迁移问题。

  5. CSRF中间件: 如果你使用CsrfExemptSessionAuthentication,你应该注释掉django.middleware.csrf.CsrfViewMiddleware。这没有任何实际作用,只不过让后端不会向前端发送无意义的csrfkey。

  6. 关于自定义认证类: 因为Django安全第一的设计理念,开启sessionkey时会自动开启csrf认证,因此必须要覆写掉csrf的认证函数才能实现只使用sessionkey进行认证。当然,如果你不在意的话,可以直接用自带的’SessionAuthentication’。