控制台
博客/开发者/正文
基于 Authing 实现细粒度的权限管理
Authing 官方2021-01-29阅读 471

授权的含义

通用领域内,授权是领导者通过为员工和下属提供更多的自主权,以达到组织目标的过程。
计算机领域内,授权是由信息系统指定批准机构授予某实体处理、存储或传送信息的权力。
而在身份认证领域内,授权是指当客户端经过身份认证后,能够有限的访问服务端资源的一种机制。
 

为什么要进行「授权」?

在已经构建起的用户系统中,当你的 API 需要判断当前访问用户是否能访问当前资源时,就需要你构建自己的权限系统了。授权是权限系统中一个很重要的概念,是指判断用户具备哪些权限的过程,这与认证完全不同。
 
对于企业来说,授权能够明确组织成员之间的关系,使职责和边界变得更加清晰,方便公司管理;同时,授权能够保障数据安全、防控风险,不同的权限准许不同的操作,可防止用户人为破坏、数据泄漏、误操作等事故的发生;授权能够提高决策的效率,优秀的授权和权限管理使系统更易操作,使员工的工作效率得到提升。
 
而从产品角度出发,授权可以保障产品系统的使用安全和数据安全,防止违规操作和数据泄漏;授权也可以提高系统的可操作性,提升用户体验;此外,好的授权功能会提升产品价值,使其在市场上更具有竞争力。
 

授权模式

授权模式主要为两种,分别是通过基于 OAuth 2.0 流程中的授权码模式,以及通过 API 接口到授权中心对用户授权进行集中验证。
 

基于 OAuth 2.0 框架的授权模式

OAuth2 框架是一种安全、轻量、标准的授权体系,用于帮助资源方、调用方、资源所有者之间的完成授权流程。如果授权过程中不涉及到资源所有者,可以使用 client_credentials 模式。这种模式一般用于后端服务器的 M2M 模式。你可以在应用详情页获取应用的 ID 和密钥,你需要将其并安全地存储在你的服务器中。
 
你可以使用 OAuth2.0 的 client_credentials 模拟颁发具备特定 scope 权限的 access_token:
 
curl --request POST \
  --url https://${YOUR_AUTHING_DOMAIN}/oidc/token \
  --header 'accept: application/json' \
  --header 'cache-control: no-cache' \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data 'grant_type=client_credentials&scope=customScope&client_id=CLIENT_ID&client_secret=CLIENT_SECRET'
 
Authing 会根据调用方请求的资源和上下文环境,动态的决定颁发具备哪些权限的 AccessToken。并返回被拒绝的 scope:
 
{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "scope": "user",
  "scope_rejected": "xxx yyy"
}
 
其中 scope 为该 access_token 具备的权限列表,用空格分割。你可以在后端通过 scope 判断用户具备哪些权限。
 
当授权流程中涉及到需要资源所有者参与授权时,可以使用 OAuth2.0 框架中的授权码模式。你需要将权限项目放在发起授权的链接的 scope 参数中,例如:
 
https://${YOUR_AUTHING_DOMAIN}/oidc/auth?client_id={你的应用 ID}&scope=openid book:read book:delete&redirect_uri={你的业务回调地址}&state={随机字符串}&response_type=code
 
需要让资源所有者点击链接,之后会转到登录页面,资源持有者认证自己的身份,并将资源授权授权给调用方。
 
完成认证授权后,浏览器将跳转到业务回调地址,并通过 URL 传递 code 授权码。调用者可以使用这个 code 授权码到 Authing 换取一个具备权限的 AccessToken,用于获取资源方的资源。
Code 换取 Token 的代码如下:
 
curl --request POST \
  --url https://${YOUR_AUTHING_DOMAIN}/oidc/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data client_id={应用 ID} \
  --data client_secret= {应用密钥} \
  --data grant_type=authorization_code \
  --data redirect_uri={回调地址} \
  --data code={授权码}
 
同样,Authing 会根据调用方请求的资源和上下文环境,动态的决定颁发具备哪些权限的 AccessToken。并返回被拒绝的 scope:
 
{
  "access_token": "...",
  "token_type": "Bearer",
  "expires_in": 3599,
  "scope": "openid book:read",
  "scope_rejected": "book:delete"
}
 
当然,资源方必须在返回资源前,验证调用者是否携带了具备权限的 AccessToken,当一切检验通过,就可以安全地返回资源。
 

使用权限 API

除了使用 OAuth2.0 的 client_credentials 模式,还可以使用通用的权限 API,通过权限 API 创建角色、给角色授权角色、判断用户是否具备某个权限等。我们支持 Node.js、Python、Java、PHP、C# 等语言的 SDK,详情请见文档
 

权限模型

目前,基于角色的访问控制(RBAC)和基于属性的访问控制(ABAC)是被大家广泛采用的两种权限模型,二者各有优劣。
RBAC 模型构建起来更加简单,缺点在于无法做到对资源细粒度地授权(都是授权某一类资源而不是授权某一个具体的资源);ABAC 模型构建相对比较复杂,学习成本比较高,优点在于细粒度和根据上下文动态执行。
 

基于角色的访问控制(RBAC)

什么是 RBAC?

基于角色的访问控制(Role-based access control,简称 RBAC),指的是通过用户的角色(Role)赋予其相关权限,这实现了细粒度的访问控制,并提供了一个相比直接授予单个用户权限,更简单、可控的管理方式。
 
当使用 RBAC 时,通过分析系统用户的实际情况,基于共同的职责和需求,将他们分配给不同的角色。然后可以授予每个用户一个或多个角色,每个角色具有一个或多个权限,这种 用户-角色角色-权限 间的关系,让我们可以不用再单独管理单个用户,用户从具备的角色里面继承所需的权限,从而使得用户赋权这件事变得更加简单。
 

RBAC 的使用场景

以一个简单的场景为例(Gitlab 的权限系统),用户系统中有 Admin、Maintainer、Operator 三种角色,这三种角色分别具备不同的权限,比如只有 Admin 具备创建代码仓库、删除代码仓库的权限,其他的角色都不具备。
 
 
我们授予某个用户「Admin」这个角色,他就具备了「创建代码仓库」和「删除代码仓库」这两个权限。
 
不直接给用户授权策略,是为了之后的扩展性考虑。比如存在多个用户拥有相同的权限,在分配的时候就要分别为这几个用户指定相同的权限,修改时也要为这几个用户的权限进行一一修改。有了角色后,我们只需要为该角色制定好权限后,将相同权限的用户都指定为同一个角色即可,便于权限管理。
 

基于属性的访问控制(ABAC)

什么是 ABAC?

基于属性的访问控制(Attribute-Based Access Control,简称 ABAC)是一种灵活的授权模型,通过一个或一组属性来控制是否有对操作对象的权限。
 
其中,ABAC 中的 A,也就是属性(Attribute),用于表示 subject、object 或者环境特征的特点,属性使用 key-value 的形式来存储这些信息,比如我在公司的 role 是 developer,role 是 key,developer 是 value。
 
ABAC 属性通常来说分为四类:用户属性(如用户年龄),环境属性(如当前时间),操作属性(如读取)和对象属性(如一篇文章,又称资源属性),所以理论上能够实现非常灵活的权限控制。
 

ABAC 的使用场景

比如,在北京的公司员工明天将用公司内网参加会议培训。其中,「北京」、「公司内网」是环境属性,「参加会议培训」是操作属性。在需要根据这些属性来动态计算权限的时候,RBAC 授权模型将无法满足需求。这个时候就需要使用 ABAC 授权模型。
 
在 ABAC 权限模型下,你可以轻松地实现以下权限控制逻辑:
  1. 授权某编辑具体某本书的编辑权限;
  2. 当一个文档的所属部门跟用户的部门相同时,用户可以访问这个文档;
  3. 当用户是一个文档的拥有者并且文档的状态是草稿,用户可以编辑这个文档;
  4. 早上九点前禁止 A 部门的人访问 B 系统;
  5. 在除了上海以外的地方禁止以管理员身份访问 A 系统;
 
上述的逻辑中有几个共同点:
  1. 具体到某一个而不是某一类资源;
  2. 具体到某一个操作;
  3. 能通过请求的上下文(如时间、地理位置、资源 Tag)动态执行策略;
 
如果浓缩到一句话,你可以细粒度地授权在何种情况下对某个资源具备某个特定的权限
 
RBAC 授权模型是基于角色的访问控制,当面对大型企业与组织时,RBAC 授权模型需要维护大量的角色和授权关系,ABAC 授权模型则会更加灵活。此外,当资源不断增多时,RBAC 授权模型需要维护所有的相关角色,而 ABAC 授权模型只需根据相关属性进行维护,有更强的拓展性。
 

借助 Authing 实现权限模型

在 Authing 的权限系统中,我们同时支持 RBAC 和 ABAC 的权限模型,即兼顾了管理的效率和授权的粒度。在 Authing 控制台的权限管理模块中可以对资源和角色进行统一管理,并可以采用个性化的授权规则将资源的操作权限授权给角色或者某个用户。
 
 
在进行授权的时候首先选择授权用户的主体,除了可以对用户和角色进行授权,还可以对分组和某个组织节点进行授权,这种不同维度的授权模型可以给授权管理带来极大的便利。
 
我们可以给每一个授权定义授权规则——允许或拒绝某个资源在某些条件下访问。
 
 
在控制台定义好授权资源之后,你可以将 Authing 中定义的权限模型通过 API 的形式集成在自己的项目之中,实现对资源权限的控制。
 
首先初始化 Management SDK:
这里以 Node SDK 为例,我们同时还支持 Python、Java、C#、PHP 等语言的 SDK,详情请见 https://docs.authing.cn/
 
import { ManagementClient } from 'authing-js-sdk';

const managementClient = new ManagementClient({
    userPoolId: "YOUR_USERPOOL_ID",
    secret: "YOUR_USERPOOL_SECRET"
});
 
调用 managementClient.acl.isAllowed 方法,三个参数分别为:
  • userId: 用户 ID,用户可以被直接授权特定资源的操作,也可以继承角色被授权的权限。
  • resource: 资源标志符,如 repository:123 表示 ID 为 123 的代码仓库,repository:* 表示代码仓库这一类资源。
  • action: 特定操作,如repository:Delete表示删除代码仓库这个操作。
const { totalCount, list } = await managementClient.acl.isAllowed("USER_ID", "repository:123", "repository:Delete");
 
Authing 策略引擎会根据你配置的权限策略,动态执行策略,最后返回 true 或者 false,你只需要根据返回值就能判断用户是否具备操作权限。
 

总结

Authing 不仅通过用户、角色这两种对象实现了 RBAC 模型的角色权限继承,还在此之上实现了如何进行更细粒度、动态的 ABAC 权限模型。这是一个渐进的过程,随着业务的复杂性不断变大,你可以基于 Authing 强大而又灵活的权限系统,快速构建出适合你业务场景的权限模型。
 
如果你喜欢我们的内容,欢迎关注公众号「Authing 身份云」和访问我们的官网,更多有趣的内容等你来看~

文章作者

avatar

Authing 官方

0

文章总数

关注 Authing 公众号
随时随地发现更多内容
添加 Authing 小助手
加入 Authing 开发者大家庭