Django测试用例中Token认证失败:提示‘Authentication credentials were not provided’
Django APITestCase Token认证失败排查与解决
问题描述
在Django的APITestCase中创建超级测试用户并生成对应Token,发送PUT请求到/api/v1/credit-card接口时,已携带格式正确的Authorization请求头,但始终返回错误:
{'detail': ErrorDetail(string='Authentication credentials were not provided.', code='not_authenticated')}
使用相同Token在Postman中调用该接口可正常工作,尝试过改为POST请求、调用登录函数,问题仍未解决。
相关代码
test.py
class CreditCardTestCase(APITestCase): def setUp(self): self.user = User.objects.create_superuser(username='test.case', email='test.case@gmail.com', password='123456') if self.user is not None: try: self.token = Token.objects.get(user=self.user) except: self.token = Token.objects.create(user=self.user) self.headers = {'Authorization': 'Token ' + self.token.key} def test_credit_card_with_invalid_number(self): print(self.headers) response = self.client.put('/api/v1/credit-card', { 'exp_date': '02/2028', 'holder': 'Olivia Johnson', 'number': '1234567891231234', 'cvv': '999' }, **self.headers) print(response.data) self.assertDictEqual({ 'message': 'Invalid Credit Card Number' }, response.data)
view.py
class CreditCardView(APIView): authentication_classes = [TokenAuthentication] permission_classes = [IsAuthenticated] def get(self, request, key): credit_card = CreditCardModel.objects.get(pk=key) serializer = CreditCardSerializer(credit_card) private_key = read_keys('private') serializer_data = serializer.data serializer_data['number'] = decrypt_credit_card_number( private_key, credit_card.number) return Response({ 'credit_card': serializer_data }, status=HTTP_200_OK) def put(self, request): try: params = request.data credit_card = CreditCard(params['number']) response = basic_info_verifier(params, credit_card) brand_obj = CreditCardBrand.objects.get( description__icontains=response['brand']) public_key = read_keys('public') print(public_key) encrypted_number = encrypt_credit_card_number( public_key, params['number']) credit_card = CreditCardModel( exp_date=response['expiration_date'], holder=params['holder'], number=encrypted_number, cvv=params['cvv'] if params['cvv'] else '', brand=brand_obj ) credit_card.save() return Response({ 'message': 'Credit card registered with success', 'card_id': credit_card.id }, status=HTTP_201_CREATED) except BrandNotFound: return Response({ 'message': 'Brand Not Found' }, status=HTTP_400_BAD_REQUEST) except InvalidCreditCardNumberException: return Response({ 'message': 'Invalid Credit Card Number' }, status=HTTP_400_BAD_REQUEST) except InvalidDateException: return Response({ 'message': 'Invalid Expiration Date, must be later than now and valid' }, status=HTTP_400_BAD_REQUEST) except InvalidHolderException: return Response({ 'message': 'Invalid Holder Name, must have at least 2 characters' }, status=HTTP_400_BAD_REQUEST) except: return Response({ 'message': 'Error, code failed at card creation' }, status=HTTP_500_INTERNAL_SERVER_ERROR)
问题原因及解决办法
1. 请求头传递方式错误
Django测试客户端的put方法中,直接解包**self.headers会把请求头当作表单数据传递,而不是真正的HTTP请求头。正确的做法是通过headers参数传递请求头。
修正后的测试请求代码
def test_credit_card_with_invalid_number(self): print(self.headers) response = self.client.put( '/api/v1/credit-card', { 'exp_date': '02/2028', 'holder': 'Olivia Johnson', 'number': '1234567891231234', 'cvv': '999' }, headers=self.headers # 明确指定headers参数 ) print(response.data) self.assertDictEqual({ 'message': 'Invalid Credit Card Number' }, response.data)
2. 更简洁的认证方式(可选)
无需手动生成和传递Token,直接使用测试客户端的force_authenticate方法认证用户:
def setUp(self): self.user = User.objects.create_superuser( username='test.case', email='test.case@gmail.com', password='123456' ) # 直接认证当前用户,后续请求自动携带认证信息 self.client.force_authenticate(user=self.user)
这样后续发送请求时就不需要再手动设置Authorization头。
3. 优化Token生成逻辑
原代码中的Token获取逻辑可以简化为get_or_create,同时添加断言确保Token生成成功:
def setUp(self): self.user = User.objects.create_superuser( username='test.case', email='test.case@gmail.com', password='123456' ) # 简化Token获取/创建逻辑 self.token, created = Token.objects.get_or_create(user=self.user) # 断言Token存在,避免后续空值问题 self.assertIsNotNone(self.token) self.headers = {'Authorization': f'Token {self.token.key}'}
内容的提问来源于stack exchange,提问作者Augusto
相关产品推荐
相关产品推荐

