【问题标题】:Django Rest Framework request header based permission基于 Django Rest Framework 请求标头的权限
【发布时间】:2026-02-01 09:30:01
【问题描述】:

我正在使用 Django Rest Framework 构建一个 Django 应用程序,以将其托管在我组织的域中。域实现自定义身份验证协议。当有人访问域时,比如 app1.domainname.com,他们会被重定向到组织的登录页面 (login.domainname.com),并且他们必须使用其员工帐户登录。用户通过身份验证后,用户将被重定向回其初始目的地 (app1.domain.com)。然后,用户的信息存储在发送到应用程序的 HTTP 请求的一些自定义标头字段中。例如

GET / HTTP/2
Content-Type: 
User-Agent: ...
...
X-Username: johndoe1
X-Firstname: John
X-Lastname: Doe
X-Email: johndoe@domainname.com
etc.

我正在尝试为我的 REST API 实现自定义权限,以在标头中查找这些字段,然后根据用户的用户信息授权用户。这是我目前拥有的:

from rest_framework.permissions import BasePermission

allowed = ['johndoe1', 'dicksmith2', 'username3']

class CutomPerm(BasePermission):
    message = "You don't have permission to access this object"

    def has_object_permission(self, request, view, obj):
        print(request.headers)
        username = request.headers['X-Username']
        return username in allowed

但是当我运行服务器时,似乎自定义标头已传递到后端。对于某些请求,它们是,但最终用户未被授权,因为has_object_permission 方法引发了KeyError

[10/Mar/2020 10:03:29] "GET /api/obj/ HTTP/1.1" 200 81
[10/Mar/2020 10:03:29] "GET /favicon.ico/ HTTP/1.1" 200 11
{'Content-Length': '', 'Content-Type': 'text/plain', 'Host': 'localhost:8000', 'Connection': 'keep-alive', etc., 'X-Username': 'johndoe1', 'X-Firstname': 'John', etc.}
Forbidden: /api/obj/1/
[10/Mar/2020 10:04:35] "GET /api/obj/1/ HTTP/1.1" 403 6581
{'Content-Length': '', 'Content-Type': 'text/plain', 'Host': 'localhost:8000', 'Connection': 'keep-alive', etc.} # no custom headers here
[10/Mar/2020 10:04:35] "GET /favicon.ico/ HTTP/1.1" 200 11
Internal Server Error: /api/obj/1/
Traceback (most recent call last):
  File "/path/to/project/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/path/to/project/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/path/to/project/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/path/to/project/venv/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/viewsets.py", line 114, in view
    return self.dispatch(request, *args, **kwargs)
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 505, in dispatch
    response = self.handle_exception(exc)
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 465, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 476, in raise_uncaught_exception
    raise exc
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 502, in dispatch
    response = handler(request, *args, **kwargs)
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/mixins.py", line 54, in retrieve
    instance = self.get_object()
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/generics.py", line 99, in get_object
    self.check_object_permissions(self.request, obj)
  File "/path/to/project/venv/lib/python3.8/site-packages/rest_framework/views.py", line 343, in check_object_permissions
    if not permission.has_object_permission(request, self, obj):
  File "/path/to/project/project/app/permissions.py", line 11, in has_object_permission
    username = request.headers['X-Username']
  File "/path/to/project/venv/lib/python3.8/site-packages/django/http/request.py", line 388, in __getitem__
    return super().__getitem__(key.replace('_', '-'))
  File "/path/to/project/venv/lib/python3.8/site-packages/django/utils/datastructures.py", line 320, in __getitem__
    return self._store[key.lower()][1]
KeyError: 'X-Username'

请注意,在打印出来的 2 个标题字典中,第一个包含所有自定义标题,但第二个没有。 我认为这是因为在幕后发生了一些重定向,并且到达其余框架权限检查的最终请求已经丢失了所有自定义标头。无论如何要根据自定义标头检查权限吗? 谢谢

【问题讨论】:

    标签: django django-rest-framework permissions


    【解决方案1】:

    Django 修改了 http 标头键。

    您必须以如下方式访问标题:

    username = request.META.get('HTTP_X_USERNAME', None)
    if username:
        # your logic
        pass
    

    签出 Django 标头文档: https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META

    【讨论】:

    • 谢谢,但不是这样。使用 META 时出现同样的错误。此外,当无法访问标题时,我不能使用 None,因为我必须使用标题字段中的用户名,并且可以肯定它会在那里。
    • 打印 request.META 字典并检查其中的内容。如果您在那里找不到标头键,则客户端没有发送它们,或者在请求到达 Django 服务器之前某些其他服务正在删除它们。
    • 我认为是后者。我正在打印 META 字典,它与标题字典基本相同。也许某些中间件正在来回重定向,并且在此过程中省略了自定义标头。我只是不知道如何解决它。
    • 检查重定向请求的函数。它应该设置标题,还检查是否在重定向时保留了标题。如果标头不存在并回溯到丢失的位置,您应该使用 403 使请求失败。