社交网站授权
Posted
技术标签:
【中文标题】社交网站授权【英文标题】:Authorization in social networking website 【发布时间】:2011-06-13 09:18:35 【问题描述】:我需要完成以下与权限相关的事情:
我有 3 个用户:
- User A
- User B
- User C
每个用户都有以下具有相关访问设置的文档:
- User A
- Document A1, only allow contacts to view
- Document A2, allow everyone to view
- Document A3, allow no one to view except myself
- Document A4, allow contacts, and contacts of contacts to view
- User B
- Documents B1, B2, B3, B4 with similar privileges
- User C
- Documents C1, C2, C3, C4 with similar privileges
User A
有 User B
作为联系人,但不是 User C
的联系人(User B
和 User C
是联系人)。
因此,User A
将能够查看以下内容:
- Document B1 (contacts can view)
- Document B2 (everyone can view)
- Document B4 (contacts of contacts)
- Document C2 (everyone can view)
- Document C4 (contacts of contacts)
我有兴趣了解如何处理这些权限。我也在寻找任何可以帮助我起步的文档或文章。
【问题讨论】:
您要查找的词是“授权”。 任何从概念角度讨论授权的链接(不一定是 django 特定的)都会很棒。 【参考方案1】:不幸的是,Django 的授权系统不允许您为每个对象分配权限,只能为每个类分配权限。在这里,我假设您的每个“文档”都是模型类的一个实例。
不过,有一些可重复使用的应用程序可以大大简化这项任务。查看在对象(或行)级别上工作的 django-guardian 或 other packages。
【讨论】:
您能否解释一下“每个对象”、“每行”或“每个类”权限的含义,并分别举例说明?谢谢。 “按类”是指 django 管理和身份验证机制的默认方法。如果您有一个名为“Document”的模型并转到您网站的管理页面,您应该能够编辑用户并分配“yourapp | document | Can add document”(以及“can change”和“can delete ”)。但是,这适用于所有 Document 实例。另一方面,通过“每个对象”(或“每行”),您应该能够为每个单独的文档(A1、A2 等)指定权限(例如更改、删除或您可能创建的其他权限)。 附加说明:如果您的文档始终有所有者并且只有 4 个可见级别(私人、联系人、联系人的联系人和公共),您可以将级别存储为模型中的字段并使用@jammon 链接中的机制来强制执行这 4 个级别。如果您认为权限在未来可能会变得更加复杂,您应该寻找更精细和可扩展的授权系统。【参考方案2】:一般的答案是找出文档所有者与给定联系人之间的距离。在计算机科学术语中,这是directed graph。
http://techportal.inviqa.com/2009/09/07/graphs-in-the-database-sql-meets-social-networks/ 上有一篇很好的文章,其中包含一些涵盖此主题的 SQL 查询。与其试图总结整篇文章,不如将问题概念化:
从一张白纸开始。 在页面某处为每个人(在本例中为用户 A、B 和 C)绘制一个点。在 CS 术语中,这是一个“节点”。 从用户向其所有联系人绘制一个箭头。在 CS 术语中,这是“有向边”或“弧”。 这在问题中没有明确说明,但看起来用户 C 必须是用户 B 的联系人,或者是用户 A 的其他联系人中的另一个联系人(因为用户 A 可以读取 C2 和 C4)。 因此,在这种情况下,您将从用户 A -> 用户 B 和用户 B -> 用户 C 中提取。顺便说一句,如果“接触”是相互的,您可以画一条线段(或双向箭头)而不是箭头。在 CS 术语中,这将是一个“无向”与“有向”图。 Facebook 关系是一种无向关系;如果某人是我的朋友,那么我也是他们的朋友。相比之下,如果有人在我的 Outlook 通讯录中,我不一定在他们的通讯录中。所以这是有向关系。
随着越来越多的用户被添加到绘图中,您会注意到用户的联系人距离我们只有一步之遥,而他们的联系人的联系人距离他们也只有两步之遥。但是你只能沿着箭头的方向前进。
所以联系人的问题是,“我如何找到图形距离为 1 的所有节点?”而contacts-of-contacts的问题是,“我如何找到所有图距离为2的节点?”。尽管“两个或更少”可能更合适,因为您希望直接联系人可以访问所有“联系人的联系人”内容。
对于一般情况,本文中描述的一些 SQL 查询可能会提供一些见解。但对于您的特定需求,我会考虑只使用一些连接。
让我们考虑一个Users
表,主键id
及其其他字段,以及一个只有两列的HasContact
表:userId
和contactId
。我们假设用户 A 的 id 为 1,用户 B 为 2,用户 C 为 3。HasContact 有 (1, 2) 和 (2, 3) 行来表示上述关系。
一组非常简单的 SQL 连接可以生成所有朋友或所有朋友的朋友的列表。
以下查询将返回用户联系人的所有 ID:
SELECT contact.id
FROM Users "user"
LEFT JOIN Relationships "rel"
ON user.id = rel.userid
LEFT JOIN Users "contact"
ON rel.contactId = contact.id
WHERE user.id = $id_of_current_user
如果您知道用户 ID,授权查询可能非常简单:
SELECT count(*)
FROM Relationships "rel"
WHERE rel.userid = $document_owner_user_id
AND rel.contactid = $id_of_current_user
如果查询返回 0,那么我们知道当前用户不是文档所有者的联系人之一。
我们可以更新第二个查询以指示用户是否是联系人:
SELECT count(*)
FROM Relationships "rel_1"
INNER JOIN Relationships "rel_2"
ON rel_1.contactId = rel_2.userId
WHERE rel_1.userid = $document_owner_user_id
AND rel_2.contactid = $id_of_current_user
这应该返回非零值,只要在关系表中有 ($document_owner_user_id, X)
和 (X, $id_of_current_user)
都存在的条目。否则,它将返回零。
我知道这是一个冗长且有些间接的答案,所以如果您有任何问题,请发表评论。
【讨论】:
很抱歉没有给你赏金,直到它过期后我才看到你的回复。但是非常感谢您的链接和回复。 @David542,不用担心赏金。希望信息符合您的要求。【参考方案3】:您基本上需要的是Limit access to logged-in users that pass a test。但是带有“联系人的联系人”的部分可能会导致非常复杂的 sql 查询。我建议你重新考虑这个要求。 (我有很多我喜欢和信任的好朋友。但他们有各种各样的奇怪的人作为朋友......)
【讨论】:
【参考方案4】:您需要的是一个access control list(ACL),它会根据每个用户的网络而变化。 ACL 以及基于对象的权限在标准django.contrib.auth
模块中不可用。我实际上已经在 Django 中实现了一个 ACL,但它是基于类而不是基于对象的。在您的应用程序的上下文中,它就像User A
可以查看任何人的文档,只要他在某个授权组(比如Admins
组)。但它可以像User A
一样工作,可以查看User B
s 文档如果User A
在Contacts
组中User B
。
我可以根据自定义身份验证应用程序的模型结构来解释如何做到这一点。 User
和 Permission
模型将与标准 Django auth 应用程序相同(您可以从 models.py 复制它)。然后你需要一个模型来表示不同级别的权限(Everyone、ContactsOfContact、Contact、Myself)。这样做很简单,就像在标准 django auth 组模型中添加另一个字段一样。该字段将是同一模型的外键,表示每个组将从中继承的组。现在模型看起来像这样,
class SocialGroup(models.Model):
name = models.CharField(unique=True, max_length=150)
parent = models.ForeignKey('self')
现在您可以添加关系,例如Contacts
组可以通过将Everyone
组设置为Contacts
组的父组来查看Everyone
组可以查看的所有内容。请注意,Permission
模型没有外键关系。接下来我们需要一种方法来指定用户和组之间的关系。
class Relationship(models.Model):
user = models.ForeignKey(User)
related_user = models.ForeignKey(User)
related_by = models.ForeignKey(SocialGroup)
使用这个模型,我们可以说User A
(related_user) 与User B
(user) 相关,因为它属于User B
s Contacts
(related_by) 组。然后我们需要一种方法来为每个文档指定权限。
class DocumentPermissions(models.Model):
document = models.ForeignKey(Document)
group = models.ForeignKey(SocialGroup)
Permission = models.ForeignKey(Permission)
现在我们可以说Contacts
组拥有can_view
对Document B1
的权限。请注意,此模型应该转到 Document 模型所在的任何位置,而不是我们新的 auth 应用程序中的 auth models.py。就是这样,现在我们要做的就是编写逻辑来查找给定用户对给定文档的权限。所以当User B
试图查看Document A1
时,我们需要做的是,
User A
)
然后找到所有者和尝试从关系模型(联系人)访问文档的人之间的关系组。
然后检查DocumentPermissions
以查看该组或其继承自的任何组是否有权查看该文档。
这个逻辑可以在新的身份验证后端(它将使用我们的新身份验证模型)中实现,用于 Django,它可以进入新的身份验证应用程序。您可以在标准Django model backend 中检查has_perm
功能。这就是你需要做的。所有的装饰器和东西仍然可以工作。
【讨论】:
【参考方案5】:您可以包含“朋友的朋友”字段。这将是一张非常大的桌子(例如,200*200*N = 40,000*N ... 或者如果您对朋友没有限制,并且某人是与 100 万人的朋友,那将是非常大的 - 那就是 100 万人与 100 万个 FoF ) 但它比每次查看访问数据库 200 次要容易。
【讨论】:
以上是关于社交网站授权的主要内容,如果未能解决你的问题,请参考以下文章