首页 > 解决方案 > 无法在 Django Rest Framework 中同时使用用户令牌和 API 密钥验证测试请求

问题描述

我的项目需要对某些端点使用两种身份验证方法:

  1. 使用此应用程序的全局 API 密钥。
  2. 使用 DRF 提供的默认用户身份验证令牌

使用 Postman 或 iOS 应用程序使用 API 时一切正常,但我无法在我的测试中进行身份验证。用户身份验证工作正常,但全局密钥失败。

这是 Postman 中 HTTP 标头的外观:

X-Api-Key: KEY_VALUE
Authorization: Token TOKEN_VALUE

我尝试使用 shell 中的代码进行试验,并且使用测试中使用的完全相同的代码进行身份验证工作正常!只有在测试中它才会失败,所以我不确定如何进一步调试。

编辑:

你可以在 github 上看到一个完整的项目。

测试输出:

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test (app.tests.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../authtest/app/tests.py", line 21, in test
    self.assertEqual(response.status_code, status.HTTP_200_OK)
AssertionError: 403 != 200

----------------------------------------------------------------------
Ran 1 test in 0.112s

FAILED (failures=1)
Destroying test database for alias 'default'...

当我打开外壳python manage.py shell并复制并粘贴测试代码时:

>>> from rest_framework.test import (APITestCase, APIRequestFactory, force_authenticate)
>>> from rest_framework import status
>>> from accounts.models import User
>>> from app.views import MyView
>>> user = User.objects.create(email="test@test.com")
>>> user.set_password('1234')
>>> factory = APIRequestFactory()
>>> API_KEY = "KIkKSSz7.ziURxOZv8e66f28eMLYwPNs7eEhrNtYl"
>>> headers = {"HTTP_X_API_KEY": API_KEY}
>>> request = factory.get('/myview', **headers)
>>> force_authenticate(request, user=user)
>>> response = MyView.as_view()(request)
>>> response.status_code
200

也向邮递员提出请求。知道这里发生了什么吗?

标签: djangodjango-rest-framework

解决方案


问题是API_KEY您在测试中传递的硬编码不是导致状态 403 的有效密钥。我认为原因可能是 django 在运行测试时使用了单独的测试数据库,而 djangorestframework-api-key 使用 APIKey 模型生成 api_key。

您可以通过以下方式确认:

  1. 从 MyView 中的权限类中删除 HasAPIKey 并再次运行测试。
  2. 或通过在您的测试中将空字符串作为 API_KEY 传递,从而导致您现在遇到相同的错误。

所以我可能建议生成一个新的有效 api_key 并传递它而不是硬编码密钥。

在这里,我将分享如何进行测试的更新测试文件

from rest_framework.test import (APITestCase, APIRequestFactory, force_authenticate)
from rest_framework import status
from accounts.models import User
from .views import MyView
from rest_framework_api_key.models import APIKey


class MyTests(APITestCase):

    def test(self):
        user = User.objects.create(email="test@test.com")
        user.set_password('1234')
        user.save()
        factory = APIRequestFactory()
        api_key, key = APIKey.objects.create_key(name="my-remote-service")
        headers = {"HTTP_X_API_KEY": key}
        request = factory.get('/myview', **headers)
        force_authenticate(request, user=user)
        response = MyView.as_view()(request)
        self.assertEqual(response.status_code, status.HTTP_200_OK)


推荐阅读