仅资料所有者可更新数据:DRF APIView权限问题求助
解决Django REST Framework中用户可修改他人UserInfo的权限问题
问题根因
你的patch方法直接通过pk查询UserInfo实例后就允许修改,完全没有校验该UserInfo是否属于当前登录用户——这就导致只要知道他人UserInfo的ID,任意登录用户都能篡改其数据。
分步解决方案
1. 新增权限校验逻辑
在patch方法中,获取到UserInfo实例后,必须验证其关联的user是否等于当前请求的request.user,不匹配则返回403权限拒绝。
2. 修复序列化器漏洞
当前序列化器允许修改user字段,恶意用户甚至可以把自己的UserInfo关联到其他用户,需要把user字段设为只读。
3. 异常处理与规范优化
- 用
get_object_or_404替代直接get,避免因无效ID抛出未捕获的异常 - 修正状态码:PATCH请求成功应返回
200 OK(201 CREATED是POST创建资源的标准状态码) - 类名遵循PEP8规范(驼峰命名)
4. 可选:优化模型关联(推荐)
从业务逻辑看,一个用户应该对应唯一的UserInfo,建议把ForeignKey改成OneToOneField,更符合实际场景,同时简化查询逻辑。
优化后的完整代码
views.py
from django.shortcuts import get_object_or_404 from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.permissions import IsAuthenticated from .models import UserInfo from .serializers import UserInfoSerializers class GetUserInfo(APIView): permission_classes = [IsAuthenticated] def get(self, request, format=None): # 如果改成OneToOneField,这里可以直接用request.user.userinfo userinfos = request.user.userinfo_set.all() serializer = UserInfoSerializers(userinfos, many=True) return Response(serializer.data) def patch(self, request, pk, format=None): # 自动处理ID不存在的情况,返回404 user_info = get_object_or_404(UserInfo, id=pk) # 核心权限校验:当前用户只能修改自己的UserInfo if user_info.user != request.user: return Response( {"detail": "你没有权限修改此用户信息"}, status=status.HTTP_403_FORBIDDEN ) serializer = UserInfoSerializers(instance=user_info, data=request.data, partial=True) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from django.contrib.auth.models import User from rest_framework import serializers from .models import UserInfo class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ('id', 'first_name', 'username') class UserInfoSerializers(serializers.ModelSerializer): user = UserSerializer(read_only=True) # 设置为只读,禁止修改关联用户 class Meta: model = UserInfo fields = ('id', 'picture', 'profession', 'user')
models.py(可选优化)
from django.contrib.auth.models import User from django.db import models class UserInfo(models.Model): # 改成OneToOneField,确保一个用户对应唯一的UserInfo user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, related_name='userinfo') picture = models.ImageField(upload_to="profile_pics", null=True) profession = models.CharField(max_length=200, null=True) def __str__(self): return f"{self.user}'s Profile Picture"
urls.py(对应类名修改)
from django.urls import path from rest_framework_simplejwt.views import TokenRefreshView from . import views urlpatterns = [ path('userinfo/', views.GetUserInfo.as_view(), name="UserInfo"), path('userinfo/<str:pk>/', views.GetUserInfo.as_view()), path('api/token/', views.MyTokenObtainPairView.as_view(), name='token_obtain_pair'), path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('register/', views.RegisterView.as_view(), name='auth_register'), ]
内容的提问来源于stack exchange,提问作者Gaurav Joshi
相关产品推荐
相关产品推荐

