如何通过Token限制Django REST API的用户访问权限?
问题描述
参考William Vincent所著《REST API’s with Django》搭建REST API,已完成书中配置,但遇到两个核心问题:
- 在
UserList类中添加authentication_classes = [TokenAuthentication]后,携带Token的用户无法正常访问API;移除该配置后,所有用户都能无限制访问,不符合"仅持有Token的用户可访问API"的需求。 - 希望进一步通过Token限制用户仅能查看API中的特定信息(例如仅能查看自身的用户信息)。
相关代码
api/views.py
from django.contrib.auth import get_user_model from rest_framework import generics, permissions from rest_framework.authentication import TokenAuthentication from .serializers import UserSerializer # Display List View - User class UserList(generics.ListAPIView): queryset = get_user_model().objects.all() serializer_class = UserSerializer permission_classes = (permissions.IsAuthenticated,) authentication_classes = [TokenAuthentication]
api/serializers.py
from django.contrib.auth import get_user_model from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): class Meta: model = get_user_model() fields =('id', 'username', 'email',)
api/urls.py
from django.urls import path from .views import (UserList) urlpatterns = [ path('users/', UserList.as_view()), ]
master_application/urls.py
urlpatterns = [ path('admin/', admin.site.urls), path('', include('users.urls')), path('api/', include('api.urls')), path('api-auth/', include('rest_framework.urls')), path('api/rest-auth/', include('rest_auth.urls')), path('api/rest-auth/registration/', include('rest_auth.registration.urls')), path('', include('django.contrib.auth.urls')), path('users/', include('users.urls')), path('users/', include('django.contrib.auth.urls')), ]
settings.py
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAdminUser', 'rest_framework.permissions.IsAuthenticated', ], 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ] }
解决方案
1. 修复Token认证后无法访问的问题
你的settings.py中DEFAULT_PERMISSION_CLASSES同时配置了IsAdminUser和IsAuthenticated,这意味着请求需要同时满足是管理员和已认证两个条件,普通携带Token的非管理员用户会被直接拒绝访问。
修改settings.py的权限配置,仅保留IsAuthenticated即可:
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', # 仅保留这一行 ], 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.TokenAuthentication', ] }
同时确保请求时正确携带Token:
- 在请求头中添加:
Authorization: Token <你的Token值> - 或通过URL参数传递:
?token=<你的Token值>
如果仍然报错,检查是否给目标用户生成了Token:可以通过Django admin查看,或执行命令生成:
python manage.py drf_create_token <用户名>
2. 限制用户仅能查看自身信息
当前UserList返回所有用户数据,要实现用户仅能查看自身信息,推荐以下两种方案:
方案一:改用RetrieveAPIView获取当前用户信息
修改api/views.py,创建返回当前请求用户数据的视图:
from django.contrib.auth import get_user_model from rest_framework import generics, permissions from rest_framework.authentication import TokenAuthentication from .serializers import UserSerializer class CurrentUserView(generics.RetrieveAPIView): serializer_class = UserSerializer permission_classes = (permissions.IsAuthenticated,) authentication_classes = [TokenAuthentication] def get_object(self): return self.request.user
更新api/urls.py:
from django.urls import path from .views import CurrentUserView urlpatterns = [ path('user/', CurrentUserView.as_view()), # 单个用户的端点,符合REST规范 ]
用户访问/api/user/时,只会返回自身的信息。
方案二:修改ListAPIView的queryset过滤当前用户
如果坚持使用ListAPIView(不推荐,列表接口返回单个数据不符合REST规范),可以通过重写get_queryset过滤数据:
class UserList(generics.ListAPIView): serializer_class = UserSerializer permission_classes = (permissions.IsAuthenticated,) authentication_classes = [TokenAuthentication] def get_queryset(self): # 仅返回当前请求用户的数据 return get_user_model().objects.filter(id=self.request.user.id)
额外扩展:管理员与普通用户的权限区分
如果需要实现"管理员可查看所有用户,普通用户仅能查看自身"的细粒度控制,可以自定义权限类:
from rest_framework import permissions class IsOwnerOrAdmin(permissions.BasePermission): def has_permission(self, request, view): # 管理员允许访问所有接口 if request.user.is_staff: return True # 普通用户仅允许访问自身信息的详情接口 return view.action == 'retrieve' and request.user.id == view.kwargs.get('pk')
然后在视图中使用该权限类:permission_classes = (IsOwnerOrAdmin,)
内容的提问来源于stack exchange,提问作者spidey677
相关产品推荐
相关产品推荐

