基于 Keycloak 的前端视图权限设计与实现
本文介绍如何利用 Keycloak 授权服务(Authorization Services),在前端实现细粒度的视图权限控制,包括页面级、按钮级、数据级和项目级权限,并给出与后端 API、数据权限模型的配合方式。
核心诉求:不用在前端写一堆 if/else,而是让权限统一落在 Keycloak 与服务端模型中,前端只负责问 Keycloak:这个人能不能看/点/改?
整体思路概览
从前端视角来看,视图权限控制大致分为三层:
- 身份与令牌:用户通过 Keycloak 登录,拿到
access_token。 - 资源与权限模型:在 Keycloak 中将「页面、按钮、视图区域」建模为 Resource / Scope,并通过 Policy、Permission 组合出业务规则。
- 前端消费权限:前端(如 Angular)在路由守卫、指令、组件逻辑中根据权限决定 页面是否可见、按钮是否可点。
在 Keycloak 中开启授权服务
要使用 Keycloak 的细粒度授权,需要在对应的 Client 上打开授权功能:
- 在
Clients中选择你的前端/资源服务器 Client。 -
打开:
Client authenticationAuthorization

开启后,菜单会出现 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
- Resource:
含义:拥有 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,当前用户有view和create两个 Scope 权限。 -
前端可以基于这份清单,决定:
- 是否展示“项目列表页面”(
view) - 是否显示/启用“新建项目”按钮(
create)
- 是否展示“项目列表页面”(
在 Angular 前端中实现视图权限控制
在 Angular 中,可以用现成的社区库来对接 Keycloak 认证和授权,例如:
keycloak-angularkeycloak-authz-angular
仓库参考:
- https://github.com/mauriciovigolo/keycloak-angular
- https://github.com/witcom-gmbh/keycloak-authz-angular
一个常见的做法是:
- 在应用启动时初始化 Keycloak,完成登录与 token 管理。
- 在路由守卫中检查权限,决定页面是否可访问。
- 通过指令或服务,在模板中控制按钮/区域显示与否。
可以设计类似的前端权限服务(伪代码思路):
// 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": [] // 无权限用户组(黑名单)
}
在实现上,可以:
- 从 Keycloak 获取用户所在的 Group 列表。
- 在查询平台数据时,基于上述白/黑名单进行过滤。
用户数据:按 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:多策略组合的评估方式
前端只需要关心两件事:
- 这个用户拥有哪些 Resource + Scope 权限?
- 据此决定:哪些页面能进、哪些区域能看、哪些按钮能点。
而数据层、项目层的权限则通过 Group、owner 等字段进一步细化,最终形成一套从 Keycloak → API → 数据的完整权限链条, 让视图权限不再靠“约定俗成”,而是有据可依、可视可配。