ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

企业级IDM落地实践:基于Keycloak实现统一认证与权限治理

企业级IDM落地实践:基于Keycloak实现统一认证与权限治理 企业数字化转型走到今天一个被反复低估、但一旦爆发就让人头疼的问题正在悄悄吞噬运维和研发团队的精力身份管理。你可能也有过这样的经历——公司内部有 OA、GitLab、Jenkins、K8s、数据平台、财务系统每个系统各有一套账号密码新员工入职要逐个开通离职三个月后账号还残留在某个冷门系统里等到审计才发现。这不是流程问题而是身份架构问题。IDMIdentity Management身份管理要解决的就是这件事。这篇文章想从一个更落地的角度切入企业级 IDM 到底应该怎么做它和统一登录、权限模型、审计合规之间的关系是什么以及我们用一套开源方案能不能跑通完整的身份治理流程。看完之后你会对 IDM 的模块边界、技术选型和实际接入代码有一个清晰的判断而不是只停留在“SSOIDM”的模糊认知上。1. 这篇文章真正要解决的问题先给一个明确判断IDM 不是单一系统而是一套覆盖“身份全生命周期”的工程体系。它要管的不只是登录而是账号从创建、变更、禁用、删除到审计归档的完整过程。很多团队第一次接触 IDM是因为单点登录SSO的需求——希望员工用一套账号登录所有内部系统。但做下去才发现SSO 只是 IDM 的入口层。真正的难点在于下游几十个系统每个系统的账号体系和权限模型都不一样组织架构在调整人员转岗、入职、离职是常态权限是动态变化的不能靠管理员手工维护审计要求记录“谁在什么时间从哪个系统访问了什么数据”。如果你正在做技术选型或者已经被内部账号管理折磨到想动手重构这篇文章会把 IDM 拆成几个可落地的模块并给出一套基于开源方案的最小实现路径。读完你至少能回答三个问题IDM 的核心模块有哪些、落地时应该先做什么、接入一个业务系统需要改哪些配置。2. IDM 的核心概念与功能拆解下面这些概念容易混淆建议先建立一张心智模型。2.1 身份是谁的问题“身份”不等于“账号”。身份是业务意义上的主体比如一个真实员工、一个外包人员、一个机器人账号。账号则是身份在某个系统中的映射。一个人可能有多个账号AD 域账号、GitLab 账号、云平台账号。IDM 的核心任务之一就是建立身份与多账号之间的关联关系确保“一个身份对应一套规范化账号集”。这个区别在离职场景中尤为关键。如果只删 LDAP 账号不清理下游系统对方依然能通过原密码登录。IDM 要做的是联动删除或禁用所有下游账号并把操作记录留痕。2.2 认证与授权的边界认证Authentication验证“你是谁”常见手段有密码、短信验证码、TOTP、证书。授权Authorization决定“你能干什么”常见模型有 RBAC、ABAC。IDM 通常同时承担认证中心和权限治理两层职责但在架构上应该把二者解耦。认证中心处理登录凭证和会话权限治理管理角色、权限点以及用户与角色的绑定。混在一起的结果是每次权限调整都要改认证配置风险极高。2.3 身份生命周期管理一个完整的身份生命周期包括创建、激活、变更、禁用、归档、删除。阶段主要动作常见失败场景创建依据 HR 或流程引擎数据自动生成账号手动建号漏建错建激活下发初始密码、绑定 MFA初始密码丢失流程断裂变更转岗时调整角色与权限旧权限残留禁用离职即时禁用所有系统账号只禁了主账号归档保留审计记录冻结会话数据被误删除删除按合规周期清理账号该删不删审计不合规2.4 身份数据源与堡垒机式查询企业内部通常有权威数据源比如 HR 系统或组织主数据。IDM 应该以权威数据源为准通过定向同步或事件推送的方式将员工和组织信息分发到下游系统。这里的重点工作是字段映射、冲突检测和脏数据清洗。IDM 本身不该成为又一个手工维护数据的系统它应该是数据的协调者和分发者。3. IDM 技术架构与常见选型3.1 分层架构一个标准的 IDM 平台逻辑上分为四层接入层提供登录页、API 网关、SDK。核心服务层认证服务、授权服务、用户管理、组织管理、密码策略、会话管理。数据层用户库、角色库、审计日志库、配置库。集成层连接器Connector负责与 AD/LDAP、HR 系统、GitLab、K8s 等下游系统交互。3.2 自研与开源怎么选很多团队会纠结 IDM 要自研还是用开源。我的建议是不要轻易自研认证核心。身份认证的安全门槛很高涉及会话管理、凭证存储、混淆注入、重放攻击等大量细节自研成本远超预期。更稳妥的组合是“开源身份认证引擎 自研业务连接器”。常见开源方案包括方案定位适合场景Keycloak开源 IAM支持 OIDC/OAuth2/SAML中小团队、需要快速落地Apache Directory ServerLDAP 目录服务已标准化 LDAP 的组织casdoor国产开源 IAM中文文档友好国内业务系统多、团队不喜欢英文文档Zitadel云原生 IAMGo 实现容器化程度高的团队注意方案没有绝对好坏要和团队的技术栈、运维能力、合规要求匹配。下面实操部分以 Keycloak 为例因为它最普及、资料多、接入方式典型。4. 环境准备与前置条件为了避免示例写成“不可复制的片段”这里给出一个最小可运行的环境组合。版本细节建议以官方文档为准不要盲目照抄因为 Keycloak 的版本迭代很快。4.1 基础环境建议准备Linux 服务器或者本地 Docker Desktop 环境Docker 和 Docker ComposeJava 开发环境用于编写接入客户端MySQL/PostgreSQL用于存放 Keycloak 元数据一个测试用的业务系统我这里用 Spring Boot 写一个最小 Web 应用来演示接入。4.2 规划域与客户端在 Keycloak 中“Realm”和“Client”是两个容易混淆的基础概念Realm一个隔离的身份域类似租户内部包含用户、角色、客户端配置。Client一个接入应用每个需要认证的业务系统对应一个 Client。更稳妥的规划方式是一个环境统一使用一个 Realm比如company每个业务系统注册为一个 Client。不要在环境之间互相混杂 Realm否则后续迁移和权限治理会变得混乱。5. Keycloak 环境搭建与基础配置我们先用 Docker Compose 把 Keycloak 和 PostgreSQL 跑起来。5.1 docker-compose.ymlversion: 3.8 services: postgres: image: postgres:15 container_name: idm-postgres environment: POSTGRES_DB: keycloak POSTGRES_USER: keycloak POSTGRES_PASSWORD: keycloak123 volumes: - postgres_data:/var/lib/postgresql/data networks: - idm-network keycloak: image: quay.io/keycloak/keycloak container_name: idm-keycloak command: start-dev environment: KC_DB: postgres KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak KC_DB_USERNAME: keycloak KC_DB_PASSWORD: keycloak123 KEYCLOAK_ADMIN: admin KEYCLOAK_ADMIN_PASSWORD: admin123 ports: - 8080:8080 depends_on: - postgres networks: - idm-network volumes: postgres_data: networks: idm-network: driver: bridge执行docker-compose up -d启动成功后访问 http://localhost:8080 。首次进入时用admin / admin123登录 Admin Console。出于安全考虑生产环境必须修改默认管理员密码并禁用start-dev模式。5.2 创建 Realm 和客户端在 Admin Console 中完成以下操作点击左上角下拉框选择Create Realm输入名称company。在Clients菜单中点击Create client客户端 ID 填写demo-app客户端协议选择openid-connect。在Access settings中设置Valid redirect URIs为http://localhost:8081/*这是业务系统登录后的回调地址。保存后在Credentials标签页记录下Client secret后面会用。5.3 创建测试用户和角色在Users菜单中创建用户zhangsan设置初始密码并关闭Temporary选项。然后创建两个角色developer和ops。给用户zhangsan分配developer角色。这一步看起来简单但在真实项目中是关键路径角色命名、分配关系、权限点映射建议从一开始就纳入规范管理不要随手创建。6. 完整示例Spring Boot 接入统一登录业务系统接入 Keycloak 的方式很多可以用 Spring Security 的 OAuth2 客户端也可以自己对接 OIDC 协议。这里用 Spring Boot 3 Spring Security 演示最标准的授权码模式。6.1 pom.xml 依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-oauth2-client/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency /dependencies6.2 application.yml 配置Keycloak 已经实现了 OIDC ProviderSpring Boot 可以直接把它当作一个普通的 OAuth2/OIDC 服务来对接没必要引入 Keycloak 的旧版适配器。server: port: 8081 spring: application: name: demo-app security: oauth2: client: registration: keycloak: provider: keycloak client-id: demo-app client-secret: ${KEYCLOAK_CLIENT_SECRET} authorization-grant-type: authorization_code scope: openid, profile, email provider: keycloak: issuer-uri: http://localhost:8080/realms/company authorization-uri: http://localhost:8080/realms/company/protocol/openid-connect/auth token-uri: http://localhost:8080/realms/company/protocol/openid-connect/token user-info-uri: http://localhost:8080/realms/company/protocol/openid-connect/userinfo jwk-set-uri: http://localhost:8080/realms/company/protocol/openid-connect/certs注意issuer-uri的含义它声明了当前应用信任的令牌签发方。一旦配置错误令牌验证会直接失败。实际项目中如果中间还有网关或 HTTPS 代理这里的地址必须与下游系统实际访问到的地址保持一致否则会出现“明明登录成功但回调验证一直报错”的怪问题。6.3 安全配置类// 文件路径src/main/java/com/example/idmdemo/config/SecurityConfig.java package com.example.idmdemo.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; Configuration EnableWebSecurity public class SecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth - auth .requestMatchers(/public/**).permitAll() .anyRequest().authenticated() ) .oauth2Login(Customizer.withDefaults()) .oauth2Client(Customizer.withDefaults()); return http.build(); } }这里的思路是/public/**是不需要登录的接口其余所有请求都要求先完成 SSO 登录。oauth2Login负责自动跳转到 Keycloak 登录页。6.4 获取用户信息与角色// 文件路径src/main/java/com/example/idmdemo/controller/UserController.java package com.example.idmdemo.controller; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.oauth2.core.oidc.user.OidcUser; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; RestController public class UserController { GetMapping(/user) public MapString, Object user(AuthenticationPrincipal OidcUser user) { return Map.of( username, user.getPreferredUsername(), email, user.getEmail(), roles, user.getClaimAsStringList(realm_access) ); } }运行mvn spring-boot:run浏览器访问 http://localhost:8081/user 未登录时会跳转到 Keycloak 登录页。输入刚才创建的zhangsan登录成功后会回到业务系统并返回用户信息。这条链路跑通后你已经完成了一个最小的统一认证接入后续所有业务系统都可以照着同样的模式接入。7. 运行结果与效果验证很多入门教程会停在上一步但实际验证不止“能登录”这么简单。7.1 验证登录成功浏览器输入zhangsan的账号密码后页面应该自动跳回http://localhost:8081/user并显示{ username: zhangsan, email: zhangsanexample.com, roles: [developer] }7.2 验证会话与单点退出接着再将另一个应用接入同一个 Realm登录其中任意一个系统后再访问另一个系统应该无需重新输入密码。这个能力的本质是浏览器持有 Keycloak 会话 Cookie业务系统通过令牌与 Keycloak 建立信任。退出登录时不能只清除业务系统本地会话必须跳转到 Keycloak 的全局限出地址。否则用户会误以为已经退出实际上令牌在其他系统仍然有效。http://localhost:8080/realms/company/protocol/openid-connect/logout?redirect_urihttp://localhost:8081/7.3 验证权限不足场景创建另一个只分配了ops角色的用户并尝试访问一个仅允许developer访问的接口。更合理的做法是业务系统通过角色判断是否能调用某个接口Keycloak 只负责提供身份和角色声明。对于高权限操作比如生产环境配置变更建议再叠加一层审批和操作审计不能只依赖前端隐藏按钮。7.4 验证失败时的第一排查点如果登录后出现403或invalid token第一排查顺序是看浏览器 Network 中的回调地址是否与Valid redirect URIs一致看issuer-uri是否能正常访问看 Client Secret 是否匹配看业务系统服务器时间是否正确JWT 的exp校验依赖时钟同步。8. 常见问题与排查思路问题现象可能原因排查方式解决方案登录后提示Invalid redirect_uri回调地址没有加进 Keycloak 客户端白名单检查浏览器地址栏的回调 URL 与页面配置在 Client 的 Valid redirect URIs 中补上完整地址令牌解析失败或签名校验失败JWKS 地址不可达或issuer-uri配置不一致请求 JWKS 地址比对iss声明修正issuer-uri确保服务间网络互通角色信息在业务系统里取不到没开启客户端角色映射或请求 scope 不含角色查看 ID Token 与 Access Token 内容配置 mapper将 realm roles 加进 token 声明用户离职后仍能登录部分系统下游系统未接入 Keycloak 或只做了主账号禁用检查下游系统认证方式通过连接器联动禁用所有下游账号初始密码设置后仍被要求修改Temporary 选项没有关闭查看用户属性把Temporary设为 Off 或让用户先完成修改会话经常失效访问令牌有效期设置过短查看 Realm token settings根据业务安全要求调整 Token 有效期同时刷新会话生产环境 HTTPS 下回调异常反代与 Keycloak 之间的Host头不一致查看网关转发配置正确配置KC_HOSTNAME保持内外部地址一致另外要特别提醒生产环境不要开启 Keycloak 的start-dev不要使用默认的 H2 数据库不要用弱密码。认证系统的安全底线比普通业务系统高得多任何疏漏都可能导致全平台账号泄露。9. IDM 落地最佳实践与工程建议9.1 角色与权限建模遵循最小够用原则RBAC 是多数企业最容易上手的权限模型但设计时要避免“一个人一个角色”的陷阱。角色的价值在于抽象共性否则每个角色只对应一个人权限管理会迅速失控。更合适的方式是“角色 分组 特殊授权”的组合按岗位和职责建立角色按组织架构建立分组个别特殊场景通过临时授权处理。9.2 账号生命周期自动化优先于手工操作任何一次手工建账号、手工加角色都是审计漏洞的温床。与 HR 系统打通数据源后员工入职自动建号、转岗自动变更角色、离职自动禁用账号。自动化策略要分阶段上线先做到离职自动禁用这步收益最大且风险最小再逐步覆盖入职、转岗和权限变更。9.3 密码策略与多因素认证要分层对于普通内部应用至少要求密码长度 12 位以上并支持 MFA。对于核心运维系统比如数据库、K8s、云控制台建议强制使用 TOTP 或 WebAuthn。密码策略要区分普通用户与管理员管理员账号的认证强度必须高于普通用户。9.4 审计日志不能只存不查IDM 产生的审计日志至少包括登录请求、令牌签发、权限变更、用户创建与删除、MFA 绑定与解绑。日志要满足两个要求防篡改保留足够长时间。更稳妥的做法是接入集中日志平台并定期做权限核验而不是等安全事故后再翻日志。9.5 连接器与依赖方向业务系统与 IDM 之间应保持单向依赖业务系统信任 IDM 签发的令牌IDM 不反向依赖业务系统的内部状态。任何业务系统需要自定义身份逻辑时应通过扩展点实现而不是直接修改认证核心。从工程角度IDM 的 API 应该少而稳定连接器做到可插拔。9.6 灰度与回滚认证系统改动对全平台影响最大。接入新 IDM 或调整认证策略时建议先对少数内部系统灰度再逐步扩大范围。回滚方案必须包括签发令牌的兼容期、旧登录态的有效期、以及对已经登录用户的会话处理策略。不要等到上线日才发现“用户回不去了”。10. 总结与后续学习方向这篇文章从 IDM 的边界讲起先说清楚身份管理不是单一登录然后落到 Keycloak 的最小落地实现再延伸到角色建模、审计和安全策略。回到最开始的问题IDM 高不高门槛如果只是搭一个 SSO门槛确实不高但要做到身份生命周期自动化、权限治理和合规审计它就是一个需要长期迭代的企业基础工程。对于正在选型或即将落地的团队我的建议是先跑通最小闭环用开源方案 一个真实业务系统把统一登录和离职禁用做扎实再逐步扩展边界。不要一开始就追求大而全身份治理的工程价值是慢慢累积出来的。更值得继续深挖的方向包括OIDC 协议细节与安全边界、SAML 与旧系统的对接、SCIM 标准的账号自动供给、企业级 ABAC 权限模型以及基于零信任理念的持续认证。这些内容每一项都足够写成长文建议作为后续学习路径持续关注。
返回列表