
GitHub: https://github.com/geekchow/micro-service-authKeycloak是本 PoC 的身份提供方Identity Provider它对用户进行认证、拥有会话状态并签发驱动一切下游授权决策的 JWT。什么是 IdPIdP身份提供方是负责认证用户并签发令牌的系统。完整术语表见 01 — 概念。简而言之IdP 证明用户是谁其他系统决定该用户能做什么本项目为什么需要 IdP如果没有 IdP每个服务都得各自实现并维护自己的登录逻辑、令牌签发逻辑、密码处理、用户存储与声明管理。那会造成重复的安全代码各服务之间不一致的身份规则更难的审计与排查更难与网关、策略引擎集成本项目使用Keycloak作为 IdP从而把身份集中化一个统一的地方认证用户一个统一的地方管理像customer、ops-admin这样的角色一个统一的地方签发 JWT一个统一的地方定义像customer_id、account_ids这样的声明Keycloak 在本 PoC 中的位置User or demo scriptKeycloakJWT access tokenKongOPAbanking-api-serviceidentity-bootstrap-serviceKeycloak不是网关、不是策略引擎、也不是银行 API。它是身份与令牌声明的来源。Keycloak 如何工作Realmrealm 是一个安全边界拥有自己的用户、client、角色与令牌设置。本项目使用 realmbanking-poc。所有演示用户与 client 都位于该 realm 内。用户用户是用于登录的身份。本 PoC 中的演示用户是aliceops-adminidentity-bootstrap-service通过 Keycloak 管理 API 创建并管理这些用户设置如下属性customer_idaccount_idsdemo_managed角色角色代表粗粒度的权限分组。本 PoC 使用两个 realm 角色customerops-admin这些角色出现在 JWT 中被Kong、OPA与banking-api-service使用。Clientclient 是与 Keycloak 交互的应用或服务。本仓库定义了两个mobile-banking-app—— 公共 client演示登录流程用它来获取访问令牌directAccessGrantsEnabled: true、publicClient: truekong-introspection—— 机密的服务账户 clientpublicClient: false、serviceAccountsEnabled: true由Kong用来内省令牌协议映射器Protocol Mappers协议映射器控制哪些声明进入令牌。mobile-banking-app映射了customer_id—— 来自用户属性写入访问令牌、ID 令牌与 userinfoaccount_ids—— 来自用户属性多值写入访问令牌、ID 令牌与 userinfo受众mobile-banking-app—— 仅写入访问令牌这些声明会流向下游Kong把它们转发给OPAOPA在策略中使用它们banking-api-service用它们做纵深防御检查。令牌登录成功后Keycloak 签发一个签名的 JWT 访问令牌包含subpreferred_usernameaudissexprealm_access.rolescustomer_idaccount_ids该令牌由 Keycloak 签名从而让下游系统能够以密码学方式校验它。本项目中的 OAuth 2.0 与 OpenID ConnectKeycloak 讲的是标准的身份协议。OAuth 2.0OAuth 2.0 是一套用于委托访问的框架。它提供了获取访问令牌、并在调用 API 时使用这些令牌的标准方式。OpenID ConnectOpenID Connect 是建立在 OAuth 2.0 之上的身份层。它标准化了身份声明与端点。实践中Keycloak 认证用户Keycloak 签发兼容 OIDC 的 JWT服务用这些令牌来识别调用方这里使用的流程演示脚本使用直接的用户名/密码令牌交换directAccessGrantsEnabled来对接 Keycloak。对本 PoC 而言重要的是凭据发送给 KeycloakKeycloak 校验凭据Keycloak 返回一个签名的 JWT 访问令牌该令牌被发送到Kong以及银行 API 路径上Keycloak 的会话与令牌活跃性Keycloak 拥有 JWT 背后的实时会话状态 —— 这正是为什么即便对一个结构上合法的 JWT令牌内省也可能回答active: false。完整的内省机制见 11 — JWT 签名、校验与内省。逻辑会话类型Keycloak 跟踪三层会话状态认证会话authentication session短暂—— 登录流程期间的临时状态登录完成或过期后移除用户会话user session—— 表示某个 realm 中被认证的用户跟踪开始时间、空闲/过期状态与登出状态客户端会话client session按 client 划分—— 为每个 client如mobile-banking-app附着在某个用户会话上跟踪该 client 在此次登录会话中的参与情况物理存储运行时Keycloak 把在线会话状态存放在 Infinispan 缓存中。在集群部署里这些缓存分布在各节点上。离线会话则持久化在数据库中。关键的实践要点令牌声明随 JWT 一同传输但实时会话状态存放在 Keycloak 的服务端。这正是为什么一个令牌可以被正确解码而内省仍可能返回active: false。关于访问令牌/刷新令牌的生命周期、以及它们与会话状态的关系详见 13 — 访问令牌与刷新令牌的生命周期。令牌签发与校验流程banking-api-serviceKongKeycloakUserbanking-api-serviceKongKeycloakUserusername passwordauthenticate usersigned JWT access tokenAPI request bearer tokenintrospect tokenactive or inactiveforward allowed requestfetch JWKS (for signature validation)validate issuer audience signature expiryAPI response为什么登录之后仍然需要校验 JWT一个常见的误解“Keycloak 已经认证过用户了所以服务不需要再次校验令牌。”这是错的。一旦令牌离开 Keycloak下游系统仍必须验证该令牌确实由 Keycloak 签发、是发给本应用的、尚未过期、且未被篡改。这正是为什么本 PoC 在多个地方校验令牌Kong用 Keycloak 做内省banking-api-service校验 JWT 的签名、签发者与受众Keycloak 在本仓库中如何配置主配置文件是infra/keycloak/realm-export.json。从该文件核实的关键设置设置值Realmbanking-pocRealm 角色customer、ops-admin公共 clientmobile-banking-appdirectAccessGrantsEnabled: true机密 clientkong-introspectionserviceAccountsEnabled: true协议映射器customer_id、account_ids用户属性audmobile-banking-app用户档案属性customer_id单值、account_ids多值—— 仅管理员可编辑realm-export 还声明了用户档案user-profileschema以便identity-bootstrap-service能安全地写入customer_id与account_ids属性。Keycloak 如何与其他组件协作Keycloak 与 identity-bootstrap-servicebootstrap 服务使用 Keycloak 管理 API 来创建演示用户设置密码分配 realm 角色设置自定义属性customer_id、account_ids、demo_managed这让 PoC 无需手动配置 Keycloak 即可重复运行。Keycloak 与 KongKong在转发请求或询问OPA决策之前使用 Keycloak 令牌内省来检查令牌是否处于 active。Kong使用kong-introspection机密 client 的凭据向 Keycloak 认证。Keycloak 与 OPAOPA不直接与 Keycloak 通信。取而代之的是Keycloak 在 JWT 中签发声明Kong在内省后读取经校验的令牌上下文Kong把相关声明发给OPAOPA在策略中使用这些声明所以 Keycloak 是通过令牌声明间接影响OPA的决策。Keycloak 与 banking-api-servicebanking-api-service信任 Keycloak 作为令牌签发者但会独立校验签名通过 JWKS签发者受众然后读取诸如realm_access.roles、customer_id、account_ids之类的声明用于服务端的授权检查。本 PoC 中的声明流转issues JWT with roles customer_id account_idsintrospect tokensend claims to OPAallow or denyforward requestvalidate JWT againuse claims for defense in depthKeycloakKongOPAbanking-api-serviceResponse本 PoC 中的实际示例示例 1alice 访问她自己的账户Keycloak 为alice签发一个令牌包含角色customercustomer_idC-1001account_ids[A-1001]随后Kong确认令牌处于 activeOPA看到A-1001在令牌声明中banking-api-service再次检查同一组声明请求被允许示例 2alice 访问另一个账户如果alice请求账户A-2001该令牌仍然是一个有效的身份令牌但声明并不授权访问A-2001OPA返回 denyKong返回403这体现了关键思想有效的身份并不自动意味着有效的授权。示例 3ops-admin 的访问如果用户具备ops-admin角色OPA允许更宽的访问banking-api-service也看到该角色并在服务端允许访问为什么 Keycloak 在这里很合适Keycloak 提供了集中化的认证标准的令牌签发OIDC/OAuth 2.0通过协议映射器支持自定义声明用于自动化演示初始化的管理 API面向网关与服务的标准集成模式它让项目能够专注于银行授权逻辑而不必重新发明登录基础设施。常见误解“Keycloak 已经处理了所有安全”不。Keycloak 处理身份与令牌签发。它不替代网关执行Kong、策略评估OPA或业务服务检查banking-api-service。“OPA 可以替代 Keycloak”不。OPA是 PDP —— 它判定策略。它不认证用户、也不签发令牌。“Kong 可以替代 Keycloak”不。Kong是 PEP —— 网关。它不是身份提供方。“Spring Boot 自己全干了不就行”技术上它能做更多但那样会把身份、策略与业务逻辑塌缩到一个地方使架构更难维护、更难理解。小结Keycloak 认证用户Keycloak 签发携带身份与权益声明的 JWTKong检查令牌活跃性并向OPA请求授权决策banking-api-service再次校验 JWT 并执行服务端检查这就是 Keycloak 在本项目中的意义它是那个以标准、集中、可重复的方式让其余安全流程得以成立的系统。← Prev: 05 — 组件巡览 · Next: 07 — Kong → 返回专栏目录