阅读 302

系统权限设计 - 基本概念和思路

权限系统的设计几乎是每个系统都必需的模块,最近对系统的权限设计有一些心得体会。遇到过一些坑,也有一些思考,所以想写下来分享给大家。

本文的目的是帮助大家理清楚权限设计中的一些基本概念,提供常用的权限系统设计思路。

首先,我们抛出一个案例,读者可以想一想如果自己来设计权限系统,如何满足这个需求?

需求描述:

在一个大学里,有许多人。学生即将进行期末考试。对于某个期末考试的试卷,有以下权限管理需求:

  • 在整个过程,该课程的老师全程可以查看试卷
  • 在考试中,该课程的监考老师可以查看试卷
  • 在考试中,该课程的学生可以查看和写入自己的试卷
  • 在考试后,该课程的老师可以写入试卷
  • 在考试后,该年级的教务主任可以查看这个年级所有课程的试卷
  • 在整个过程,打扫清洁的阿姨不能查看/写入试卷
  • 在整个过程,校长可以查看任何试卷

备注:考试过程分为考试前、考试中、考试后。一个人可能有多个角色。

UML图:

课程考试权限问题UML.jpg

认证与授权

系统的权限控制可以分为两部分:认证和授权。两者是相对独立的概念,把它们拆开有助于我们更好地理解权限系统的设计。

认证 Authentication

所谓认证,就是判断你是不是你本人。通俗地说,就是我们系统中常见的“登录”这一步。这个验证的手段和技术有很多种,比如:表单验证、HTTP Basic验证、基于cookie的remember-me、OAuth2、甚至是指纹验证等等都是可以的。

关于各种认证方式的技术细节本文就不细讲了,这部分不在本文重点。读者只需要知道的是,权限控制的第一阶段就是认证,如果认证通过了,就说明用户登录进了系统。至于后续的各种业务的权限控制,就不归认证管了。

授权 Authorization

认证不管业务的权限控制,那谁来管呢?那就是授权了。也就是本文要讨论的重点。

首先我们需要思考一个问题:我们系统的权限设计应该有一个什么大的目标?我认为是兼顾“灵活”和“易管理”。也就是说,我们的权限设计应该能够灵活应对变化,包括用户的变化、业务权限要求的变化。但同时,它又应该比较容易管理,这样才能够让我们的权限足够清晰,不至于混乱。

所以基于上述目标,我认为权限系统的设计应该有以下三个原则:

够用就行

这里强调的是权限系统应该在满足需求的前提下尽量简单,不要过度设计。比如对于我的个人博客网站来说,只需要有我自己一个用户,那就没必要做很复杂的权限控制。

粒度适中

这里的“粒度”指的是权限的粒度。一个权限如果粒度过粗,就会导致多个业务场景使用一个权限,灵活性很差;而权限如果粒度过细,就会让权限变多,难以管理。

推荐以业务操作为权限的粒度。一个业务操作对应一个权限。业界也有DDD(领域驱动设计)等工具来帮助更好地划分业务。

比如我们上述案例中,虽然老师和学生都有“写入试卷”的操作,但其实通过理解业务,我们可以把它分为“答题”和“打分”。

那API与业务操作是一一对应的吗?多数业务场景应该是的,但也有例外。比如发微信朋友圈,就需要at人,上传图片、已经真正保存自己要发送的内容这三个操作组成“发朋友圈”这个业务,也就对应了三个业务。

这里根据笔者的实践来看,更推荐使用一个权限,可以访问这三个API。或者三个权限,分别对应相应的API,但有“权限组”的概念,把它们三个加进一个权限组,实际分配权限的时候就分配这个权限组就行了。不推荐仅仅只是三个权限分别控制三个API。

尽量使用白名单

这个是众所周知的安全原则之一。我们在实现权限系统的时候,应该尽量使用白名单,而不是黑名单。比如:有xx权限的人才能进行xx业务。而不是:没有xx权限的人就能进行xx业务。

主流的权限设计模型

权限设计是一个不容易的事,但基本上所有的系统都需要权限设计。实际上,业界已经有一些主流的权限设计模型了。这里只简单介绍一些常用的。

ACL

访问控制列表,多用于简单的权限控制。尤其在网络流量控制那块用得很多,很少用在业务系统上。

RBAC

基于角色的访问控制(Role-Based Access Control),权限与角色相关联,用户通过分配适当角色而得到这些角色的权限。也是业界最主流的权限设计模型。RBAC不能很好地检查与“数据状态”相关的权限,比如“试卷在考试前,只有老师能查看”这种需求。需要自己把角色写死,然后在代码里写逻辑检查。

ABAC

基于属性的访问控制(Attribute-Based Access Control),为了解决上述问题,业界有一种叫ABAC的解决方案。通过写动态的DSL来判断数据的状态,通过一些自定义的语法来做权限控制。

ABAC有时也被称为PBAC(Policy-Based Access Control)或CBAC(Claims-Based Access Control)。ABAC通常用于平台级的系统。比如AWS、阿里云等“云提供商”,他们有海量的资源、角色,需要很灵活的权限管理系统。

ABAC的实现成本很高。管理上也比较复杂。

示例(阿里云的RAM):

{
    "Version": "1",
    "Statement":
    [{
        "Effect": "Allow",
        "Action": ["oss:List*", "oss:Get*"],
        "Resource": ["acs:oss:*:*:samplebucket", "acs:oss:*:*:samplebucket/*"],
        "Condition":
        {
            "IpAddress":
            {
                "acs:SourceIp": "42.160.1.0"
            }
        }
    }]
}
复制代码

权限设计演进

本着上面提到的够用就行的原则,权限系统不应该过度设计。我们可以根据权限需求从简单到复杂,大概分为三类:

  • 只需要一个管理员,管理所有内容,比如个人博客网站。
  • 只有一些用户和角色,且角色所拥有的权限相对稳定。适用于绝大多数系统。
  • 有很多的用户和角色,且角色的权限很容易变化。平台级系统,如AWS。

因为第二种适用于绝大多数日常开发的项目,所以后面主要讨论的是第二种。如果是第三种的话,推荐基于ABAC开发出一套定制化的权限系统。

区分Access与Validation

先来澄清一些这两个名词的概念:

  • Access:我能不能call这个API,与数据无关,可以在网关这一层就拦截。
  • Validation:我能Call这个API,但到底有没有相应的权限,与业务数据有关,可以写Validator来验证,推荐放在最下层service。

很多第一次做权限设计的朋友,容易把他两搞混,设计出来的权限就可能看起来很混乱。

我们再来分析一下案例,就拿“读取试卷”这个业务操作来说,我们可以有一个Access叫做READ_PAPER,把它赋予给学生、老师、监考老师、教务主任、校长这几种角色。代表他们可以call这个业务API。而进到具体的验证逻辑的时候,就需要去查数据库取出试卷的状态、对应的班级、监考老师ID等等信息,通过Validation去验证这些数据,当前角色是否可以访问。

下篇文章会更具体地从前后端的视角、权限的粒度和代码层面来给出一套推荐的权限设计方案。

下篇预告:《系统权限设计-推荐方案》

关注下面的标签,发现更多相似文章
评论