7.2 权限系统与分组


文档摘要

7.2 权限系统与分组 本节摘要:授权是认证之后的第二道闸门。Django 为每个模型自动生成增删改三种默认权限,支持自定义权限与分组,校验可以挂在视图、类视图、模板三个层面。本节把这些机制用"作者只能改自己的文章"这个真实需求串起来,并给出对象级权限的边界说明。 默认权限从哪来 执行迁移后,每个模型自动获得四个权限:新增、修改、删除,外加"查看"。权限赋给用户或分组,校验时统一走 hasperm: 权限编码的格式是"应用名加权限代号"。这些权限由迁移自动创建,第 8 章 Admin 的权限面板看到的正是它们——体系一以贯之。 墨迹博客还需要"作者"这样的角色,逐个用户赋权不可维护,分组解决: 分组的价值在"角色"语义:入职作者组、离职移出组,个人用户一个权限都不用碰。

7.2 权限系统与分组

本节摘要:授权是认证之后的第二道闸门。Django 为每个模型自动生成增删改三种默认权限,支持自定义权限与分组,校验可以挂在视图、类视图、模板三个层面。本节把这些机制用"作者只能改自己的文章"这个真实需求串起来,并给出对象级权限的边界说明。

默认权限从哪来

执行迁移后,每个模型自动获得四个权限:新增、修改、删除,外加"查看"。权限赋给用户或分组,校验时统一走 has_perm:

user.has_perm("blog.add_article") # 能发文吗 user.has_perm("blog.delete_article") # 能删文章吗 user.user_permissions.set([perm_obj]) # 赋权(一般不这么手工干)

权限编码的格式是"应用名加权限代号"。这些权限由迁移自动创建,第 8 章 Admin 的权限面板看到的正是它们——体系一以贯之。

墨迹博客还需要"作者"这样的角色,逐个用户赋权不可维护,分组解决:

from django.contrib.auth.models import Group, Permission authors = Group.objects.create(name="authors") authors.permissions.add( Permission.objects.get(codename="add_article"), Permission.objects.get(codename="change_article"), ) user.groups.add(authors) # 用户入组即继承组权限 user.has_perm("blog.add_article") # True,来自分组

分组的价值在"角色"语义:入职作者组、离职移出组,个人用户一个权限都不用碰。分组本身还能属于分组外的逻辑体系——权限合并是并集,个人权限与各组权限相加即总权限。

自定义权限

默认三种不够表达"发布"这种业务动作时,在模型 Meta 里声明:

class Article(models.Model): ... class Meta: permissions = [ ("publish_article", "可以发布文章"), ]

下一个迁移会创建 blog.publish_article 权限。权限只回答"有没有这个资格",资格背后的动作(把 status 改为已发布)仍是业务代码——权限是开关,不是实现。

校验的三种挂法

挂法一:函数视图装饰器。

from django.contrib.auth.decorators import permission_required @permission_required("blog.add_article", raise_exception=True) def article_create(request): ... # 无权限直接 403

不带 raise_exception 时未授权用户被重定向到登录页;已登录但无权限时才需要 raise_exception 把语义改成本次拒绝。

挂法二:类视图 Mixin。 第 4 章预告过:

from django.contrib.auth.mixins import PermissionRequiredMixin class ArticleCreateView(PermissionRequiredMixin, CreateView): permission_required = "blog.add_article" form_class = ArticleForm

与之同族的还有 LoginRequiredMixin(只要求登录)与 UserPassesTestMixin(自定义断言函数)。

挂法三:模板内判断。 控制界面元素的显隐:

{% if perms.blog.add_article %} <a href="{% url 'blog:article-create' %}">写文章</a> {% endif %}

⚠️ 模板判断只管"藏按钮",必须与视图层校验同时存在——模板藏得住按钮,藏不住手工构造的 POST 请求。安全的顺序是:视图层校验是闸门,模板判断只是体验优化。三层关系一句话:权限校验唯一可信的位置在服务端。

对象级权限:内置体系的边界

默认权限是模型级的:有 change_article 就能改任何文章。墨迹博客的诉求是"作者只能改自己的",这叫对象级权限,内置体系不直接提供。轻量场景的正解是在视图里显式过滤:

class ArticleUpdateView(UserPassesTestMixin, UpdateView): def test_func(self): obj = self.get_object() return obj.author == self.request.user \ or self.request.user.has_perm("blog.change_article")

查询层面同样能设防——作者列表页只给自己的文章:

def my_articles(request): qs = Article.objects.filter(author=request.user)

过滤即权限:数据根本查不出来,比查出来再判断再遮掩更安全也更省事。权限规则复杂到跨模型组合时,再考虑 django-guardian 这类第三方对象权限库——它把"用户对单个对象"的授权也存成数据库记录,与 Admin 权限面板同构。引入前先掂量:多数内容型项目的对象级需求,用"过滤加 UserPassesTestMixin"已经覆盖。

权限判定层级

权限判定层级

权限审计的例行化

权限体系跑起来之后,它自身也会积累债务:人员调动后忘了移出旧组、临时开的个人权限没人回收、为某次活动设的组在活动结束后空转。这些残留像房子里多年不用的钥匙——平时无感,某天被发现时你已经不确定谁能进哪个门。治理办法是把权限审计例行化:每季度导出一份组到成员、成员到特殊权限的全量清单,逐行问三个问题——这个人还在这个角色吗,这项个人权限还有必要吗,这个组还有成员吗。第一次审计通常会清理出两位数的残留项,之后每季的维护量就降到分钟级。权限体系与代码一样,不审计就会腐化;区别是代码腐化表现为报错,权限腐化表现为沉默的越权——后者发现时往往已经太晚。

本节要点回顾

  • 默认权限每模型四种,编码"应用名加代号",迁移自动创建。
  • 分组承载角色语义,权限并集合并,人员变动不动个人。
  • 校验三挂法:装饰器、Mixin、模板 perms,模板层只是体验层。
  • 对象级权限用过滤加断言起步,规则跨模型再上权限库。

至此"谁在用系统"的问题闭环。下一章给运营人员配一套现成后台——Admin。

收尾前再给一个实践提醒:权限体系要与组织一同演化,但演化节奏应该慢于组织。墨迹博客第一版只有两组(作者、编辑),运营三个月后提出"实习编辑只能改不能发"的需求,当时没有立刻加组,而是先用"自定义权限加现有编辑组"顶了两周,确认这个角色会长期存在、而非一人一时的特殊安排,才正式建组。权限组一旦设多,管理成本与误配风险同步上升——每加一个组,都要能回答"这个组三个月后还有成员吗"。权限体系的最优形态往往是略显保守的形态,留出让角色自然浮现的时间,比预先设计一张庞大权限表更健康。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U