Django REST框架认证设置指南


Django REST Framework (DRF) 提供了强大而灵活的认证系统,能够满足各种API安全需求。本文将全面介绍DRF中的认证机制,包括基本配置、常用认证方式以及高级定制方法。

一、DRF认证基础

1. 认证与权限的区别

在开始之前,需要明确两个重要概念:

  • 认证(Authentication):验证用户身份(你是谁)
  • 权限(Permission):确定用户能做什么(你能访问什么)

本文重点讲解认证系统的配置与使用。

2. 默认认证设置

DRF的默认认证设置在settings.py中配置:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication'
    ]
}

二、内置认证方案

DRF提供了多种内置认证方式,适用于不同场景。

1. Session认证

适用于前端与Django后端在同一域名下的情况(如传统web应用)。

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
    ]
}

特点

  • 使用Django的session后端
  • 需要CSRF保护(前端需要包含CSRF token)

2. Basic认证

简单的HTTP基本认证,适合测试环境但不推荐生产使用。

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
    ]
}

特点

  • 用户名密码通过Base64编码传输
  • 不安全,除非配合HTTPS使用

3. Token认证

简单的基于令牌的认证(DRF内置)。

INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ]
}

使用流程

  1. 运行python manage.py migrate创建token表
  2. 为用户创建token:
   from rest_framework.authtoken.models import Token
   token = Token.objects.create(user=user)
   print(token.key)
  1. 客户端在请求头中添加:
   Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b

4. JSON Web Token (JWT) 认证

更现代的基于JWT的认证,需要安装第三方包。

安装配置

pip install djangorestframework-simplejwt
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ]
}

添加路由

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    ...
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

客户端使用

  1. 获取token:
   curl \
     -X POST \
     -H "Content-Type: application/json" \
     -d '{"username":"admin","password":"admin"}' \
     http://localhost:8000/api/token/

返回示例:

   {
     "access": "eyJhbGciOiJIUz...",
     "refresh": "eyJhbGciOiJIUz..."
   }
  1. 访问API:
   Authorization: Bearer eyJhbGciOiJIUz...

三、自定义认证方案

当内置认证不能满足需求时,可以创建自定义认证类。

1. 基本自定义认证类结构

from rest_framework import authentication
from rest_framework import exceptions

class CustomAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        # 获取认证凭证
        auth_header = request.META.get('HTTP_X_CUSTOM_AUTH')

        if not auth_header:
            return None  # 不认证,交给下一个认证类处理

        try:
            # 验证逻辑
            user = self.validate_token(auth_header)
        except InvalidToken:
            raise exceptions.AuthenticationFailed('Invalid token')

        return (user, None)  # 返回(user, auth)元组

    def validate_token(self, token):
        # 实现你的验证逻辑
        ...

2. API Key认证示例

class APIKeyAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        api_key = request.META.get('HTTP_X_API_KEY') or request.GET.get('api_key')

        if not api_key:
            return None

        try:
            user = User.objects.get(apikey__key=api_key)
        except (User.DoesNotExist, APIKey.DoesNotExist):
            raise exceptions.AuthenticationFailed('Invalid API key')

        return (user, None)

3. 混合认证示例

class HybridAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        # 尝试JWT认证
        jwt_auth = JWTAuthentication()
        try:
            return jwt_auth.authenticate(request)
        except exceptions.AuthenticationFailed:
            pass

        # 尝试API Key认证
        api_key_auth = APIKeyAuthentication()
        try:
            return api_key_auth.authenticate(request)
        except exceptions.AuthenticationFailed:
            pass

        # 所有认证方式都失败
        return None

四、认证策略最佳实践

1. 生产环境推荐配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

2. 视图级别的认证设置

可以在视图级别覆盖全局认证设置:

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    authentication_classes = [SessionAuthentication]
    permission_classes = [IsAuthenticated]

    def get(self, request):
        ...

3. 认证性能优化

  • 对于频繁验证的JWT,考虑使用缓存
  • 限制认证尝试次数防止暴力破解
  • 使用无状态认证减少数据库查询
from django.core.cache import cache

class CachedJWTAuthentication(JWTAuthentication):
    def authenticate(self, request):
        # 先从缓存获取用户
        user = self.get_cached_user(request)
        if user:
            return (user, None)

        # 缓存未命中,走正常认证流程
        return super().authenticate(request)

    def get_cached_user(self, request):
        auth_header = request.META.get('HTTP_AUTHORIZATION')
        if not auth_header:
            return None

        try:
            prefix, token = auth_header.split()
            if prefix.lower() != 'bearer':
                return None

            user_id = cache.get(f'jwt_user_{token}')
            if user_id:
                return User.objects.get(id=user_id)
        except:
            return None

五、常见问题解决方案

1. CSRF与Session认证

当使用Session认证时,需要处理CSRF保护:

// 前端示例:获取CSRF token
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
const csrftoken = getCookie('csrftoken');

// 在请求头中添加
fetch('/api/endpoint/', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRFToken': csrftoken
    },
    body: JSON.stringify(data)
})

2. 处理过期token

对于JWT,可以配置简单的过期处理:

# settings.py
from datetime import timedelta

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=15),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,
}

3. 多类型用户认证

处理不同类型的用户(如普通用户、管理员、第三方应用):

class MultiUserAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        # 尝试JWT认证
        try:
            jwt_auth = JWTAuthentication()
            user, _ = jwt_auth.authenticate(request)
            if user and user.is_active:
                return (user, None)
        except:
            pass

        # 尝试API Key认证(第三方应用)
        try:
            api_auth = APIKeyAuthentication()
            user, _ = api_auth.authenticate(request)
            if user and user.is_service_account:
                return (user, None)
        except:
            pass

        return None

六、安全注意事项

  1. 始终使用HTTPS:特别是在生产环境中传输认证凭证
  2. 合理设置token过期时间:平衡安全性与用户体验
  3. 保护敏感端点:关键操作应要求重新认证
  4. 实现速率限制:防止暴力破解攻击
  5. 定期轮换密钥:特别是JWT的签名密钥

通过本文的介绍,你应该已经掌握了Django REST Framework中各种认证方式的配置和使用方法。根据你的应用场景选择合适的认证策略,并始终牢记安全最佳实践,才能构建出既安全又好用的API服务。

,

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注