Django Rest Framework APIClient登录成功但访问接口仍返回401
问题:DRF测试中登录成功但访问受保护接口仍返回401
我编写了测试函数验证用户登录功能,使用DRF的APIClient完成登录,打印client.login(username='instructor', password='asdf')返回True,表明用户已成功登录,但调用受保护的recommend-list接口的GET请求仍返回401状态码。
测试函数代码
def test_user_authentication(self): client = APIClient() user_data = { 'username': 'instructor', 'email': 'myemail@example.com', 'password': 'asdf' } client.post(reverse('user-list'), user_data, format='json') self.assertTrue(User.objects.filter(username='instructor').exists()) print(client.login(username='instructor', password='asdf')) response = client.get(reverse('recommend-list')) self.assertEqual(response.status_code, 200)
用户创建视图集代码
class UserViewSet(viewsets.ViewSet): def create(self, request): serializer = UserSerializer(data=request.data) User = get_user_model() if serializer.is_valid(): validated_data = serializer.validated_data try: user = User.objects.create_user( username=validated_data['username'], email=validated_data['email'], password=validated_data['password'] ) return Response({'id': user.id, 'username': user.username}, status=status.HTTP_201_CREATED) except IntegrityError: return Response({'error': 'Username is already taken.'}, status=status.HTTP_400_BAD_REQUEST) else: return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
受保护的推荐接口视图集代码
class RecommendViewSet(viewsets.GenericViewSet): authentication_classes = [BasicAuthentication] permission_classes = [IsAuthenticated] def list(self, request): user_id = request.user.id movies = recommend_movies_for_user(user_id) return Response({"message": movies}, status=status.HTTP_200_OK)
用户序列化器代码
class UserSerializer(serializers.ModelSerializer): id = serializers.IntegerField(read_only=True) username = serializers.CharField(max_length=150) email = serializers.EmailField() password = serializers.CharField(write_only=True) follower_list = serializers.SerializerMethodField() following_list = serializers.SerializerMethodField() class Meta: model = User fields = ['id', 'username', 'email', 'password', 'follower_list', 'following_list'] def retrieve(self, instance): return instance def create(self, validated_data): user = User.objects.create_user(**validated_data) return user def get_follower_list(self, obj): follower = obj.followers_set.all() return FollowSerializer(follower, many=True).data def get_following_list(self, obj): following = obj.following_set.all() return FollowSerializer(following, many=True).data
问题原因与解决方案
原因
APIClient的login()方法默认是为SessionAuthentication设计的,它会设置会话cookie,但你的RecommendViewSet只配置了BasicAuthentication,不会识别会话登录状态,所以即使login()返回True,接口依然会要求Basic Auth凭证,返回401。
解决方案
有两种可行的修改方式:
方案1:让推荐接口支持SessionAuthentication
修改RecommendViewSet的authentication_classes,同时包含SessionAuthentication:
from rest_framework.authentication import SessionAuthentication, BasicAuthentication class RecommendViewSet(viewsets.GenericViewSet): authentication_classes = [SessionAuthentication, BasicAuthentication] permission_classes = [IsAuthenticated] # 其余代码不变
这样测试里的client.login()就能正常生效,接口会识别会话登录状态。
方案2:在测试中手动添加Basic Auth凭证
保持接口的authentication_classes不变,修改测试函数,使用client.credentials()添加Basic Auth的HTTP头:
import base64 def test_user_authentication(self): client = APIClient() user_data = { 'username': 'instructor', 'email': 'myemail@example.com', 'password': 'asdf' } client.post(reverse('user-list'), user_data, format='json') self.assertTrue(User.objects.filter(username='instructor').exists()) # 添加Basic Auth凭证 credentials = base64.b64encode(b'instructor:asdf').decode() client.credentials(HTTP_AUTHORIZATION=f'Basic {credentials}') response = client.get(reverse('recommend-list')) self.assertEqual(response.status_code, 200)
这种方式直接符合接口要求的BasicAuthentication认证方式,不需要修改接口配置。
内容的提问来源于stack exchange,提问作者Aibek Minbaev
相关产品推荐
相关产品推荐

