Django 项目中负责权限控制的模块是 contrib.auth
。有时为了扩展 行级权限 功能,还会引入一个名为 Guardian
的包。本文的描述都基于 Django + DRF + Guardian 的组合。
model
Django 并不严格遵守 RBAC 的模式,他的“权限”既可以分配给人,也可以分配给组。Guardian 也一样,model 定义如下:
class Permission(models.Model):
name = models.CharField(_('name'), max_length=255)
content_type = models.ForeignKey(ContentType, models.CASCADE)
codename = models.CharField(_('codename'), max_length=100)
class Group(models.Model):
name = models.CharField(_('name'), max_length=80, unique=True)
permissions = models.ManyToManyField(Permission)
class UserPermission(models.Model):
user = models.ForeignKey(User)
permission = models.ForeignKey(Permission)
class UserObjectPermission():
user = models.ForeignKey(user_model_label, on_delete=models.CASCADE)
permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_pk = models.CharField(_('object ID'), max_length=255)
content_object = GenericForeignKey(fk_field='object_pk')
class GroupObjectPermission()
group = models.ForeignKey(Group, on_delete=models.CASCADE)
permission = models.ForeignKey(Permission, on_delete=models.CASCADE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_pk = models.CharField(_('object ID'), max_length=255)
content_object = GenericForeignKey(fk_field='object_pk')
其中 Permission
表的 codename
字段是一种动宾结构,类似 view_user
这样,代表有 User 表的读权限。而 UserObjectPermission
表是在一个 Permission
表的外键基础上,又增加了 object_pk
的信息。因此这个表可以变得非常大,使用时需注意。
Backend
Backend
的功能定位,说的直白一些就是:
查表判断某人是否拥有某种权限。
每种 Backend 负责某种特定的表,或者查表规则。
class ObjectPermissionBackend(object):
supports_object_permissions = True
supports_anonymous_user = True
supports_inactive_user = True
def authenticate(self, username, password):
return None
def has_perm(self, user_obj, perm, obj=None):
pass
def get_all_permissions(self, user_obj, obj=None):
pass
class ModelBackend:
def get_all_permissions(self, user_obj, obj=None):
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = set()
user_obj._perm_cache.update(self.get_user_permissions(user_obj))
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
return user_obj._perm_cache
def has_perm(self, user_obj, perm, obj=None):
if not user_obj.is_active:
return False
return perm in self.get_all_permissions(user_obj, obj)
PermissionClass
Permission Class 是 DRF 提供的一种高级权限定义方法。基本定义是:
class BasePermission(object):
"""
A base class from which all permission classes should inherit.
"""
def has_permission(self, request, view):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
def has_object_permission(self, request, view, obj):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True
DRF 的 View 会在恰当的时候,比如调用 get_object
之前对用户的权限进行校验。
配置方法
PermissionClass
配置在 DRF 的 View 上,permission_classes = []
。如果没有配置,则默认使用 settings.REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES
。
当用户请求某个 view 的时候,DRF 这样进行校验:按顺序调用 PermissionClass,只有全部返回 True 才算授权成功。
Backend
配置在 settings.AUTHENTICATION_BACKENDS
这个 list 里面,代表启用这些 Backends。这个列表里的 Backends 可以被 django.contrib.auth.auth.get_backends()
函数获取到。最常用的内建 Backends 调用方有 user.has_perm()
方法,他会迭代每个 backends,任一 backend 返回 True 则校验通过,但如果任意 Backend raise PermissionDenied
,则 has_perm
直接返回 False。即对于单一 Backend 来说:
- 返回 True 一定通过
- 返回 False 不一定拒绝
- raise PermissionDenied 一定拒绝
综上,配置好 DRF 权限控制的核心就是处理好 Backend 和 PermissionClass 的关系。他俩分属后端和前端。大体可以按以下规则划分:
- 需要查数据库的属于 Backend 的范畴
- 多个权限的逻辑连接,比如
IsStaffOrReadonly
这种,属于 PermissionClass 的范畴 - 能够影响
user.has_perm()
返回结果的是 Backend,被user.has_perm()
影响的是 PermissionClass - Backend 是全局、客观、通用的,PermissionClass 是部分应用、配置生效的。
举例:
Case1 逻辑权限
指由某种“规则”定义的权限,而不是存储在数据库里,如实现这样一种需求:
如果用户拥有 Model1 的 A 权限,则其自动拥有 Model2 的 B 权限。否则没有权限。
我们分析这个需求需要直接影响 user.has_perm(Model2, B)
的返回结果,因此应该以 Backend 来实现。
来源:oschina
链接:https://my.oschina.net/lionets/blog/4466306