升级到 django 5 后,我们的测试套件开始表现出奇怪的行为。我们正在使用DRF的APIClient,并且身份验证不再按预期工作。某些 API 请求返回
rest_framework.exceptions.NotAuthenticated
,状态代码为 403(顺便说一句,如果有人可以解释为什么此状态是 403 而不是 401,我将不胜感激)。
这看起来是任意的。在用户权限等方面实现相同的 2 个视图请求中,其中一个仅在测试期间返回 403 。在 Postman 中执行相同的调用不会重现该错误。 因此,由于上述错误,一些过去在 django 4 下通过的完全相同的测试用例在 django 5 下不再通过。我没能在一个最小的例子中重现这些案例。
然而,我复制的内容如下:
client.force_authenticate(user)
client.get(url) # the user does not have permissions. response is 403, as expected
client.force_authenticate(None) # "log out"
user.user_permissions.add(permission)
client.force_authenticate(user)
response = client.get(url) # the now has permissions. response 200 is expected
assert response.status_code == 200 # response status is 403
软件包版本是
django==5.0.3
djangorestframework==3.15.0
pytest==7.4.3
pytest-django==4.7.0
我做了一个最小项目,可以观察到这一点。
据我测试,这种意外行为在 django 4 和 5 中都会发生。 我在这里使用测试客户端是否不正确?
ModelBackend 在第一次访问后缓存用户对象的权限 需要获取它们进行权限检查的时间。这是 通常适合请求-响应周期,因为权限不是 通常在添加后立即检查(在管理中,对于 例子)。如果您正在添加权限并立即检查它们 之后,例如在测试或视图中,最简单的解决方案是 从数据库中重新获取用户。解决方案:
client.force_authenticate(user=user)
client.get("/test/")
client.force_authenticate(user=None)
user.user_permissions.add(permission)
# Getting a new user instance.
# user.refresh_from_db() does not clear cache
user = get_object_or_404(User, pk=user.id)
client.force_authenticate(user=user)
response = client.get("/test/")
assert response.status_code == 200
关于未经身份验证的请求返回的 response.status_code
,DRF 文档中有以下相关信息:
当未经身份验证的请求被拒绝时,有两种情况 可能合适的不同错误代码。如果设置中未指定DEFAULT_AUTHENTICATION_CLASSES,则使用默认的:HTTP 401 未经授权
HTTP 403 权限被拒绝
HTTP 401 响应必须始终包含 WWW-Authenticate 标头,该标头指示 客户端如何进行身份验证。 HTTP 403 响应不包括 WWW-验证标头。
将使用的响应类型取决于身份验证 方案。尽管可以使用多种身份验证方案,但仅 可以使用一种方案来确定响应的类型。首先 确定类型时使用视图上设置的身份验证类 的回应。
[
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
]
禁用 SessionAuthentication 类后,未经身份验证的请求将返回 401 响应。