如何在由“站点”框架提供支持的多个 Django 站点中获取唯一用户?

Posted

技术标签:

【中文标题】如何在由“站点”框架提供支持的多个 Django 站点中获取唯一用户?【英文标题】:How to get unique users across multiple Django sites powered by the "sites" framework? 【发布时间】:2010-11-27 02:37:56 【问题描述】:

我正在构建一个 Django 站点框架,它将为多个独立站点提供支持,所有站点都使用相同的应用程序但具有自己的模板。我计划通过使用多个设置文件并为它们设置唯一的 SITE_ID 来实现这一点,就像 Django 文档中建议的 django.contrib.sites framework

但是,我不希望站点 A 的用户能够登录站点 B。检查由 syncdb 创建的用户表后,我看不到可能将用户限制到特定站点的列。我还尝试在一个站点上创建一个用户“bob”,然后使用 shell 命令列出另一侧的所有用户,果然,bob 出现在那里。

如何确保所有用户都被限制在各自的网站上?

【问题讨论】:

好问题 - 不知道这是否可能,但也许其他人知道得更好。 【参考方案1】:

最兼容的方法是创建一个包含站点模型外键的user Profile model,然后编写一个custom auth backend 来检查当前站点与该外键的值。一些示例代码:

在 app/models.py 中定义您的个人资料模型:

from django.db import models
from django.contrib.sites.models import Site
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.OneToOneField(User)
    site = models.ForeignKey(Site)

编写您的自定义身份验证后端,继承自默认后端,例如在 app/auth_backend.py 中:

from django.contrib.auth.backends import ModelBackend
from django.contrib.sites.models import Site

class SiteBackend(ModelBackend):
    def authenticate(self, **credentials):
        user_or_none = super(SiteBackend, self).authenticate(**credentials)
        if user_or_none and user_or_none.userprofile.site != Site.objects.get_current():
            user_or_none = None
        return user_or_none

    def get_user(self, user_id):
        try:
            return User.objects.get(
                pk=user_id, userprofile__site=Site.objects.get_current())
        except User.DoesNotExist:
            return None

此身份验证后端假定所有用户都有个人资料;您需要确保您的用户创建/注册过程始终创建一个。

重写的authenticate 方法确保用户只能登录正确的站点。每次请求都会调用get_user 方法,根据用户会话中存储的身份验证信息从数据库中获取用户;我们的覆盖确保用户无法登录站点 A,然后使用相同的会话 cookie 获得对站点 B 的未经授权的访问。(感谢 Jan Wrobel 指出需要处理后一种情况。)

【讨论】:

感谢 Carl,我将使用您的实现,并希望在此过程中了解有关 custon 后端的知识。 不,这个后端只做部分工作。它不允许一个站点的用户登录到另一个站点,这还不够。用户可以登录站点 A 并手动复制身份验证 cookie 以将其与请求一起发送到站点 B。此类 cookie 将被识别为有效并且用户将登录(仅当没有有效的会话 cookie 时才调用身份验证后端)。要完成该解决方案,您需要一个中间件,该中间件将为您的站点的每个请求调用,并检查用户是否登录到与请求相关的站点,而不是其他站点。 @JanWrobel 我相信,您可以在身份验证后端的 get_user 方法中实现类似的检查,而不是单独的中间件。在每个请求上都会调用此方法,而不仅仅是在登录时。我将更新答案以包含此内容;感谢您指出遗漏。 @CarlMeyer 覆盖 get_user 有效,并且确实比单独的中间件简单得多。谢谢! 但是如何处理独特的字段?如何在不同站点中添加两个用户名为“Arti”的用户?【参考方案2】:

您可以插入考虑站点 ID 的自己的授权和身份验证后端。

请参阅 django 文档中的 other authentication sources 和 authentication backends references

除此之外,如果您的 django 源代码太旧,您始终可以自己修改 authenticate() 或 login() 代码。毕竟……这难道不是开源的奇迹之一。请注意,这样做可能会影响与其他模块的兼容性。

希望这会有所帮助。

【讨论】:

编写自定义后端当然是我考虑过的事情,但暂时拒绝了,因为我认为我没有必要的技能。修改实际的 Django 代码也是如此。 编写一个自定义的身份验证后端并不难,大概只有六行代码。在 Django 中更容易做的事情之一。为了保持最大的兼容性,请在用户配置文件模型中放置一个外键到站点,然后在您的自定义身份验证后端中关闭它。 如果不建议直接修改 Django 源,我会赞成这个答案。当自定义身份验证后端可用时,绝对没有理由这样做,甚至不应该在答案中提及。【参考方案3】:

你必须知道,很多人抱怨 Django 默认授权系统和特权——它有简单的对象规则,用于对象的实例——这意味着什么,不编写任何代码是不可能的。

但是,有一些授权钩子可以帮助您实现这一目标,例如:

看看那里: http://code.djangoproject.com/browser/django/trunk/django/contrib/auth/models.py 和类权限。

您可以添加自己的权限并为它们定义规则(用户和 ContentType 都有一个 ForeignKey)。

然而2,如果没有猴子补丁/更改某些方法可能会很困难。

【讨论】:

以上是关于如何在由“站点”框架提供支持的多个 Django 站点中获取唯一用户?的主要内容,如果未能解决你的问题,请参考以下文章

通过 Gunicorn/Nginx 使用 Django 的站点框架运行多个站点

一个 Django 实例下的多个站点 - 站点有时会提供错误的内容

Django 上的多个站点

在多个子域上共享 Django 会话的缺点

Django上的多个站点

Django框架02:模型与站点