本文介绍如何利用 Keycloak 授权服务(Authorization Services),在前端实现细粒度的视图权限控制,包括页面级、按钮级、数据级和项目级权限,并给出与后端 API、数据权限模型的配合方式。

核心诉求:不用在前端写一堆 if/else,而是让权限统一落在 Keycloak 与服务端模型中,前端只负责问 Keycloak:这个人能不能看/点/改?

整体思路概览

从前端视角来看,视图权限控制大致分为三层:

  1. 身份与令牌:用户通过 Keycloak 登录,拿到 access_token
  2. 资源与权限模型:在 Keycloak 中将「页面、按钮、视图区域」建模为 Resource / Scope,并通过 Policy、Permission 组合出业务规则。
  3. 前端消费权限:前端(如 Angular)在路由守卫、指令、组件逻辑中根据权限决定 页面是否可见按钮是否可点

在 Keycloak 中开启授权服务

要使用 Keycloak 的细粒度授权,需要在对应的 Client 上打开授权功能:

  1. Clients 中选择你的前端/资源服务器 Client。
  2. 打开:

    • Client authentication
    • Authorization

开启后,菜单会出现 Authorization,用于配置 Resource、Scope、Policy、Permission。

资源模型:把页面和按钮交给 Keycloak 管

Resource:页面 / 视图模块

我们将 Resource 视为页面或较大的视图区域。例如:

  • Projects Resource → 项目管理页面
  • Dashboard Resource → 仪表盘页面
  • Default Resource → 通用资源(可选)

在 Keycloak 中,你可以为每个页面或视图模块配置一个 Resource。

Scope:按钮 / 操作 / 子区域

Scope 用来表达对 Resource 的“操作”或子权限,例如:

  • view:查看
  • create:创建
  • edit:编辑
  • delete:删除

对于 Projects Resource,可以配置 Scope:

  • view:查看项目列表、详情
  • create:创建项目
  • edit:编辑项目
  • delete:删除项目

Policy 与 Permission:把“谁能做什么”写清楚

Policy:业务规则的抽象

Policy 是访问条件,可以基于用户、用户组、角色等定义。例如:

  • User-based:指定某几个用户
  • Group-based:某个用户组中的成员
  • Role-based:具有某个角色的用户

示例策略:

  • Project Policy - Only team leader

    • 类型:Role-based
    • 条件:拥有 team_leader 角色的用户

Permission:将资源 + 策略绑在一起

Permission = 被保护的对象(Resource/Scope) + 一组 Policy。 它回答的问题是:谁可以对哪个对象执行什么操作?

例如:

  • Permission:Create - Projects Permission

    • Resource:Projects Resource
    • Scope:create
    • Policy:Project Policy - Only team leader

含义:拥有 team_leader 角色的用户可以执行“创建项目”操作。

决策策略(Decision Strategy)

当一个 Permission 上关联了多个策略时,Keycloak 提供三种评估方式:

  • Unanimous:所有策略都为 Positive 才允许访问。
  • Affirmative:只要有一个策略为 Positive 就允许访问。
  • Consensus:Positive 数量 > Negative 数量时允许访问。

这在复杂场景下会很有用,例如「管理员无条件通过 + 普通用户需满足某些条件」。

从前端获取用户拥有的 Resource / Scope 权限

用户登录后会拿到一个 access_token。在 Keycloak 中,可以通过 UMA Ticket 的方式,查询「此用户最终拥有哪些 Resource / Scope 权限」。

示例请求:

curl --location 'http:///realms//protocol/openid-connect/token' \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --header 'Authorization: Bearer ' \
  --data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:uma-ticket' \
  --data-urlencode 'audience=' \
  --data-urlencode 'response_mode=permissions'

返回示例:

[
  {
    "rsid": "6a8d5663-bfd9-48d7-a999-031f3c496eb7",
    "rsname": "Default Resource"
  },
  {
    "scopes": ["view", "create"],
    "rsid": "7b28270a-1d83-4d7a-a71a-7d1c3b3a8d18",
    "rsname": "Projects Resource"
  }
]

解释:

  • 对于 Projects Resource,当前用户有 viewcreate 两个 Scope 权限。
  • 前端可以基于这份清单,决定:

    • 是否展示“项目列表页面”(view
    • 是否显示/启用“新建项目”按钮(create

在 Angular 前端中实现视图权限控制

在 Angular 中,可以用现成的社区库来对接 Keycloak 认证和授权,例如:

  • keycloak-angular
  • keycloak-authz-angular

仓库参考:

一个常见的做法是:

  1. 在应用启动时初始化 Keycloak,完成登录与 token 管理。
  2. 在路由守卫中检查权限,决定页面是否可访问。
  3. 通过指令或服务,在模板中控制按钮/区域显示与否

可以设计类似的前端权限服务(伪代码思路):

// permission.service.ts
canAccess(resourceName: string, scope?: string): boolean {
  // 根据从 Keycloak 拿到的权限 JSON 进行判断
}

在模板中使用:

<!-- 控制整个页面是否展示 -->
<div *ngIf="permissionService.canAccess('Projects Resource', 'view')">
  <!-- 项目列表内容 -->
</div>

<!-- 控制按钮是否显示或可点击 -->
<button *ngIf="permissionService.canAccess('Projects Resource', 'create')">
  新建项目
</button>

后端 ACL:让 API 与 Keycloak 授权保持一致

视图权限只是“看得见的那一层”;更关键的是 后端 API 的访问控制 也要与 Keycloak 保持一致,避免只在前端做“遮挡”。

在 Node.js + Express 场景中,可以直接让 Keycloak 授权服务介入,例如:

app.get("/apis/me", keycloak.enforcer("user:profile"), userProfileHandler);

这里的 keycloak.enforcer('user:profile')

  • 将路由与某个 Resource/Scope(如 user:profile)绑定。
  • 在请求到达 userProfileHandler 之前,Keycloak 会根据当前用户的 token 与授权配置,判断是否允许访问。

这样可以确保:

  • 前端只要「读 Keycloak 的结果」来做视图控制;
  • 后端用同一份授权模型做 API 保护;
  • 不会出现“前端按钮隐藏了,但接口其实开放”的安全漏洞。

数据权限模型:平台数据 & 用户数据

视图/接口权限解决的是“能不能看/调这个页面 / API”,数据权限则进一步细化到「能看到哪些记录」。

平台数据:按用户组控制

平台数据基于 用户组(Group) 做白名单/黑名单控制:

  • valid_groups:白名单用户组,此用户组下的所有用户有权限使用数据。
  • invalid_groups:黑名单用户组,此用户组下的所有用户无权限使用数据。
  • 若两者都为空:视为所有用户有权限。

MongoDB 中的数据结构示例:

{
  "_id": "6473ccdb3d690cc2cafe68a4",
  "...other properties": "省略的其他字段",
  "valid_groups": [], // 有权限用户组(白名单)
  "invalid_groups": [] // 无权限用户组(黑名单)
}

在实现上,可以:

  1. 从 Keycloak 获取用户所在的 Group 列表。
  2. 在查询平台数据时,基于上述白/黑名单进行过滤。

用户数据:按 owner(用户 id)控制

对于用户个人数据,可以采用更简单的策略:

  • 每条数据记录一个 owner 字段,对应创建者的用户 id
  • 只有 owner 自己可以访问/修改这条数据。

简化结构示例:

{
  "_id": "xxxx",
  "owner": "user-id-from-keycloak",
  "...": "其他业务字段"
}

平台项目权限:组织项目 & 个人项目

结合 Keycloak 的用户组管理,可以在“项目”层面再加一层权限模型:

  • 组织项目

    • 项目关联到某个组织(用户组)。
    • 该组织下的所有用户有权查看、操作项目及项目下图层。
  • 个人项目

    • 仅创建者本人有权查看、操作项目及项目下图层。

用户组信息仍来自 Keycloak,由 Keycloak 负责维护「用户 ↔ 组织」关系,平台只要读 group 信息即可。

小结:把复杂权限变成“可读、可配、可视”的模型

通过 Keycloak 的授权服务,我们可以把原本分散在前端条件判断、后端 if/else 里的权限逻辑,集中抽象为:

  • Resource:页面 / 模块
  • Scope:按钮 / 操作
  • Policy:谁具备什么条件
  • Permission:哪些条件可以访问哪些对象
  • Decision Strategy:多策略组合的评估方式

前端只需要关心两件事:

  1. 这个用户拥有哪些 Resource + Scope 权限?
  2. 据此决定:哪些页面能进、哪些区域能看、哪些按钮能点。

而数据层、项目层的权限则通过 Group、owner 等字段进一步细化,最终形成一套从 Keycloak → API → 数据的完整权限链条, 让视图权限不再靠“约定俗成”,而是有据可依、可视可配。