如何使用两个 Django 身份验证后端并根据使用的后端提供不同的访问权限?

How can I use two Django authentication backends and give different access depending on which is used?

我有两个不同的 类 用户用于基于 Django 的事件注册系统。我们称他们为“注册人”和“职员”吧。

这两者并不相互排斥,我认为没有理由不对两者使用相同的用户模型类(用标志来区分它们)。

注册者可以通过向第三方验证身份来登录 API,然后可以注册活动。

职员可以访问 Django Admin,并且可以管理事件。我不相信第三方 API 能够充分验证具有 Staffer 访问权限的人的身份,因为虽然它确实要求提供个人信息来识别某人,但它不会要求任何像密码一样安全的东西。因此,我希望工作人员输入密码而不是通过第三方进行身份验证 API.

我发现我可以 write a custom auth backend in Django, and I can customise the default User model 满足我对上述标志的需求(尽管我怀疑默认的 is_staff)标志可能就足够了)。

不幸的是,虽然我知道我可以使用多个身份验证后端,但我仍然无法解决如何:

a) 确定要使用的身份验证后端(例如,在前端使用 Registrant 身份验证后端,在 Admin 中使用 Staffer 身份验证后端) b) 允许通过 Staffer auth 后端认证的用户访问 Admin

谁能给我指出正确的方向?

抱歉缺少代码;我正在从头开始重新设计 auth 系统(因为以前的版本真的很糟糕)我不知道从哪里开始。

编辑:在 Django Multiple Authentication Backends Based On Status一些 有用的信息,但是因为我想为用户 类 使用相同的用户模型,并且两个 auth 后端都会请求不同类型的凭据(例如,一个请求密码,另一个不请求),我需要更多地考虑如何解决这个问题。

如果我有这个问题,我会为他们每个人使用一个继承模型,将用户名作为员工的默认登录名和注册人的电子邮件,同时扩展 BaseUser 模型。

为了 select 使用 Auth 后端,我只需设置一个复选框或构建不同的 URL 即可登录。

确定要使用哪个身份验证后端”的答案非常简单,您根本不需要这样做。正如 documentation when you call the authenticate() function 中指定的那样,Django 依次尝试每个 AUTHENTICATION_BACKENDS 直到一个 return 成为用户或引发 PermissionDenied 异常。

此外,如果您查看默认后端的 code [GitHub],如果没有传入用户名或密码,它只是 returns None。您需要以类似的方式实现您的后端,这样如果您期望的凭据没有传入,您应该 return None 并让其他(可能的)后端处理它。

对于“仅允许通过 Staffer 身份验证后端进行身份验证的用户访问管理员”,您可以override the default admin site and override the has_permission method:

from django.contrib import admin
from django.contrib.auth import BACKEND_SESSION_KEY

class MyAdminSite(admin.AdminSite):
    def has_permission(self, request):
        """
        Return True if the given HttpRequest has permission to view
        *at least one* page in the admin site.
        """
        try:
            backend_path = request.session[BACKEND_SESSION_KEY]
            staffer_backend = backend_path == "path.to.StafferBackend"
            return request.user.is_active and request.user.is_staff and staffer_backend
        except KeyError:
            return False

然后在您的某个应用程序的 apps.py 中:

from django.contrib.admin.apps import AdminConfig

class MyAdminConfig(AdminConfig):
    default_site = 'myproject.admin.MyAdminSite'

最后在您的设置中替换 'django.contrib.admin':

INSTALLED_APPS = [
    ...
    'myproject.apps.MyAdminConfig',  # replaces 'django.contrib.admin'
    ...
]