Django与DRF的权限控制逻辑

编程

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 来实现。

以上是 Django与DRF的权限控制逻辑 的全部内容, 来源链接: utcz.com/z/519025.html

回到顶部