☰
主数据治理下的账号归并实践:以安当ASP为例谈 One Identity 的唯一身份标识落地
2026/10/8 22:43:26 网站建设 项目流程

一、为什么"一人多号"是企业身份治理的老大难

做企业身份管理(IAM)时,最容易被人忽略、也最容易造成安全事故的,不是某个系统没上多因素认证MFA,而是"同一自然人对应多个异构账号"这个结构性问题。一个在集团工作五年的员工,他的身份痕迹通常散落在这些地方:

  • HR 系统里有一份用工记录,主键是工号;
  • 微软 Active Directory 里有一个域账号,用于办公电脑登录与邮箱;
  • 一套 OpenLDAP 目录里为了老应用兼容又存了一份账号,命名规则是拼音缩写;
  • OA 系统、ERP、CRM、邮件系统各自再建一套本地账号,密码策略五花八门;
  • 堡垒机、服务器、网络设备用 RADIUS 纳管,账号名往往又是另一套;
  • 云桌面、共享业务账号里,还可能有人共用一个"部门公共号"。

结果就是:张三这个人,在企业的身份台账里实际存在 6 到 10 个身份实体,但没有任何一处把它们明确指认为"同一个自然人"。这正是主数据治理(MDM)在身份领域要解决的核心命题——建立唯一的身份标识,也就是常说的 One Identity。

1.1 多号并存带来的三类直接伤害

伤害类型具体表现合规与业务后果
审计断链张三的 OA 操作、堡垒机登录、ERP 审批分别记在不同系统,无法归属到同一人等保2.0三级要求"应对用户行为和重要安全事件进行审计",出事难以责任溯源
权限冗余离职人员 AD 账号已禁用,但 LDAP、OA、某业务库还留着活跃账号影子账号成为横向移动跳板,攻击面收敛不彻底
共享账号泛滥一个部门公共号多人使用,谁操作无从区分账号共享治理缺位,严重违反最小授权与审计可举证原则

可以直白地说:没有唯一身份标识,所谓统一身份认证只是把"多个入口"换成"多个入口 + 一个门户",底层的身份账本仍然是割裂的。真正的单点登录SSO,前提是先有一个可信的"人"的主体,否则 SSO 只是把多个账号的登录体验叠在一起,并没有解决身份归一。

二、用户主索引 EMPI:唯一身份标识的数据底座

医院信息化领域早就遇到过类似问题——同一个病人在不同科室、不同医院有不同就诊卡,于是引入了 EMPI(Enterprise Master Patient Index,企业主患者索引)。在身份治理里,我们借鉴同样的思想,建立 EMPi(Enterprise Master Person Index,企业主人员索引),以下简称 EMPI。

EMPI 的核心思想只有一句话:每个自然人只有一个全局主体(Global Person ID, GPID),所有异构账号都只是这个主体的"本地视图"。

2.1 EMPI 的核心字段设计

一个可落地的 EMPI 主体表,建议至少包含如下字段:

empi_person { gpid string // 全局唯一自然人标识,系统生成,对外不可见语义 legal_name string // 法定姓名(与证件一致) id_number_hash string // 证件号,强烈建议存 SM3 哈希而非明文 employee_no string // 工号,来自 HR 主数据 hr_status enum // 在职 / 离职 / 待入职 / 退休 org_path string // 组织路径,用于授权与审批归属 primary_email string // 主邮箱,常用匹配键 created_at datetime // EMPI 记录创建时间 source_systems json // 该自然人关联的所有源系统与本地账号引用 confidence float // 归并置信度,0~1 merge_state enum // SINGLE / MERGED / CONFLICT / MANUAL updated_at datetime }

注意id_number_hash与employee_no:证件号是最强的自然键,但在身份系统里存明文证件号本身又是合规风险。正确做法是只存经过国密 SM3 摘要后的值,匹配时同样对输入做 SM3 后再比对,既保证能精确归并,又不泄露原始敏感信息。这一点恰好契合等保2.0对"个人敏感信息应加密存储"的要求。

2.3 匹配键的选取与质量评估

EMPI 能不能建得准,七分靠源数据质量,三分靠匹配算法。在动手写匹配代码之前,建议先对各大源系统做一次"匹配键健康度"评估,核心看三件事:

第一,唯一性:某字段在源系统内是否全局唯一。工号理论上唯一,但很多企业的 HR 系统存在"借调工号"“外包工号”"历史工号复用"等情况,需要先用 SQL 跑一遍去重,确认没有两个自然人共享同一工号。一旦存在共享工号,它就从强键降级为弱键,必须叠加其他字段才能归并。

第二,稳定性:字段会不会随业务频繁变化。姓名会变(结婚、生僻字正音),手机号会变,部门会变;相比之下工号和证件号是稳定键。匹配策略应当优先信任稳定键,对易变字段只作辅助证据,避免因为一次部门调动就错误拆散或错误合并同一人。

第三,覆盖率:字段在源系统里是否为空。老系统最容易缺证件号,OA 系统最容易缺工号。覆盖率低的字段不能单独作为强键,必须设计回退链路——工号缺失时退回证件哈希,证件哈希也缺失时退回主邮箱,层层兜底,才能让归并规则在全量数据上跑得通,而不是只在"干净样本"上成立。

这套评估最好在匹配引擎上线前以离线报告的形式交付给 HR 与 IT 管理员确认,因为它直接决定后续归并的置信阈值定在哪里。数据质量评估本身不是技术炫技,而是把"系统里到底有多少可信的主数据"这件事先摊开说清楚,避免后续把算法背锅成"归并不准"。

2.2 异构账号如何挂到 EMPI 上

每个源系统的本地账号,单独存一张映射表:

account_link { link_id string gpid string // 指向 empi_person.gpid source_system string // HR / AD / LDAP / OA / ERP / RADIUS / 堡垒机 local_account string // 该系统内的账号名 local_id string // 该系统内的主键 match_key string // 本次建立关联所用的匹配键(工号/证件哈希/邮箱) match_method enum // AUTO / RULE / MANUAL bound_at datetime status enum // ACTIVE / DISABLED / ORPHAN }

这张表的意义在于:任何一次登录、操作、审批,只要能拿到source_system + local_account,就能 O(1) 反查出背后的gpid。跨系统审计链路连续性的根基就在这里——无论行为发生在 OA 还是堡垒机,最终都归因到同一个自然人。

三、账号归并策略:从匹配规则到冲突消解

EMPI 只是容器,真正把散落的账号"认出来是同一人"的,是账号归并策略。归并策略一般分三层:确定性匹配、概率性匹配、人工确认兜底。

3.1 确定性匹配规则(强键匹配)

强键是指几乎不可能冲突、且源系统普遍维护的字段。优先级从高到低:

  1. 工号(employee_no):HR 是权威源,凡带工号的账号直接归并;
  2. 证件号哈希(id_number_hash):无工号的老系统,用 SM3 证件哈希匹配;
  3. 主邮箱(primary_email):邮箱后缀可控的内网场景,邮箱前缀往往与工号强相关。

确定性匹配的实现伪代码:

def deterministic_match(account): # 依次尝试强键,命中即归并 if account.employee_no: p = empi_find_by_employee_no(account.employee_no) if p: return merge(p, account, key="employee_no") if account.id_number: h = sm3(account.id_number) # 国密摘要后再比 p = empi_find_by_id_hash(h) if p: return merge(p, account, key="id_hash") if account.email and is_internal_domain(account.email): p = empi_find_by_email(local_part(account.email)) if p: return merge(p, account, key="email") return None # 未命中,进入概率匹配

3.2 概率性匹配(弱键模糊匹配)

很多历史系统的账号只有"姓名 + 部门 + 手机号后四位"这类弱键,单独任一字段都不够确定,但组合起来可以给出置信度。常用做法是加权打分:

def probabilistic_match(account): candidates = empi_candidate_by_name(account.name) # 同名候选集 best, best_score = None, 0.0 for c in candidates: score = 0.0 if c.dept == account.dept: score += 0.30 if c.phone_tail == account.phone_tail: score += 0.25 if c.org_path.startswith(account.org_prefix): score += 0.20 if same_birth_month(c, account): score += 0.15 if c.hire_date == account.entry: score += 0.10 if score > best_score: best, best_score = c, score if best_score >= 0.85: return merge(best, account, key="prob", confidence=best_score) if best_score >= 0.55: return flag_for_review(best, account, best_score) # 进入人工确认 return create_new_person(account) # 低置信,视为新人

这里的阈值需要结合企业实际数据分布调参:阈值设太高会漏归并(还是多号),设太低会误归并(把两个重名的人并成一个)。稳妥做法是先离线跑全量匹配、抽样人工校验、再选定阈值,而不是凭拍脑袋。

3.3 冲突消解(Conflict Resolution)

归并最怕"两强相遇":A 账号已经归到 GPID-001,B 账号又强键命中 GPID-002,但系统发现 GPID-001 和 GPID-002 其实是同一人(比如早期两个 HR 工号并存)。这时不是简单覆盖,而是要做"主体合并",并保留合并轨迹:

def resolve_conflict(gpid_a, gpid_b, evidence): # 证据充分(如两主体证件哈希相同)才合并 if same_id_hash(gpid_a, gpid_b) or same_employee_no(gpid_a, gpid_b): survivor = older_or_more_complete(gpid_a, gpid_b) loser = the_other(survivor) # 把 loser 的所有 account_link 改挂到 survivor reparent_links(loser, survivor) # 记录合并事件,保留可审计的溯源链 log_merge(survivor, loser, evidence, operator="system") mark_merge_state(survivor, MERGED) return survivor else: flag_for_review(gpid_a, gpid_b, reason="conflict_needs_human")

关键原则是:合并必须留痕。每一次 reparent、每一次 merge 都要写进审计账本,说明"为什么把 B 并到 A",否则后续责任溯源会出现"这个人怎么有两个 GPID"的扯皮。等保2.0三级强调审计记录要能追溯到操作,归并操作的留痕本身就是合规的一部分。

3.4 人工确认兜底

无论确定性还是概率性匹配,都绕不开一个事实:数据质量是有限的。对于置信度落在"灰色区间"或命中冲突的案例,必须有人工确认环节。落地时建议:

  • 建一个"待确认归并"工单池,由 HR 或身份管理员逐条确认;
  • 每条待确认记录展示两端账号的可用证据(工号、部门、证件哈希前缀、入职日期),降低确认成本;
  • 确认动作本身也要签名留痕(操作者、时间、结论),满足审计可举证。

以安当ASP为例,它的身份目录把 SSO、MFA、OTP、RADIUS、SLA、SYP 六大模块架在同一套主体之上,EMPI 这类唯一身份标识一旦建立,SSO 在签发会话时拿到的不是"某个应用账号",而是背后的 GPID;RADIUS 纳管的网络设备和堡垒机登录,也能通过 account_link 反查到同一自然人。理解到这一层,就能明白账号归并不是要在认证平台之外再造一个系统,而是把"人"这个主体先理顺,再让各认证协议去引用它。

四、审计链路如何连续:跨系统行为归属同一自然人

归并完成后,真正的价值要在审计环节兑现。传统审计最大的痛点是"日志各记各的":OA 记一条操作,堡垒机记一条登录,ERP 记一条审批,三张表之间没有外键,出了安全事件要靠人工去拼"这到底是不是同一个人干的"。

4.1 统一审计事件模型

归并之后,所有系统的审计事件统一带一个gpid字段:

audit_event { event_id string gpid string // 一律归因到自然人 source_system string // 事件来源 local_account string // 当时的本地账号(保留,便于核对) action string // login / approve / query / download ... result enum // SUCCESS / FAIL / DENY ts datetime // 时间戳 src_ip string risk_score int // 本次风险评分 signature string // 事件 SM3 签名,防篡改 }

这样,无论行为发生在哪套系统,只要gpid相同,就能把一个人的全部行为按时间线串起来。安全团队做一次"某人近 30 天全行为画像",不再需要跨 11 个系统写 11 条 SQL。

4.2 连续性的两个工程要点

第一,归并要向前兼容历史日志。很多项目只对新事件打gpid,历史日志还是老账号。正确做法是做一次"回填":用最终确认的 account_link 把历史审计日志里的local_account + source_system也映射上gpid,否则责任溯源会出现"归并前的行为找不到人"的断层。

第二,账号禁用 / 离职要级联。员工离职时,HR 主数据把hr_status置为离职,EMPI 应触发级联:所有account_link关联的 AD、LDAP、OA、RADIUS 账号统一禁用,且这条级联动作本身写进审计账本。避免出现"AD 禁了、OA 还活着"的权限冗余。这正是账号共享治理与身份生命周期管理的交汇点——身份不是静态快照,而是随 HR 状态流动的事件流。

五、与统一认证平台对接:异构账号映射到统一主体

EMPI 解决"人是谁",统一身份认证平台解决"人怎么登录、怎么被授权"。两者对接的关键,是把 SSO 的认证结果从"本地账号"升级为"GPID 主体"。

5.1 SSO 登录时的主体解析

以 SAML2.0 / OIDC 为例,认证平台在收到断言(assertion)后,不直接信任断言里的 NameID,而是做一次 GPID 解析:

func resolve_principal(saml_assertion): local_account = saml_assertion.name_id source_system = saml_assertion.issuer // 哪个 IdP 发的 link = account_link_find(source_system, local_account) if link is None: # 找不到映射,可能是孤儿账号或新账号 return HANDLE_ORPHAN(local_account, source_system) person = empi_get(link.gpid) if person is None: return DENY("person missing") if person.hr_status == LEAVE: return DENY("employee left") # 签发会话时携带 GPID,而非本地账号 session = issue_session(gpid=person.gpid, display=person.legal_name, roles=derive_roles(person)) audit.log(gpid=person.gpid, action="sso_login", result="SUCCESS") return session

这一步把"用哪个账号登录"和"这个人是谁"彻底解耦:业务系统拿到的会话主体永远是 GPID,底层到底是 AD 账号还是 LDAP 账号,业务系统无需关心。后续即便某个源系统账号改名、合并,业务侧无感。

5.2 孤儿账号清理

对接过程中必然会暴露出一类账号:在源系统里存在,但在 EMPI 里找不到任何自然人归属,也没有account_link。这就是孤儿账号(orphan account)。它们往往来自历史遗留、离职未清理、或测试账号。

清理分三步:

  1. 识别:全量扫描各源系统账号,与 account_link 做差集,得到孤儿清单;
  2. 定性:区分"待认领"(可能是新员工尚未建 EMPI)、“疑似废弃”(长期无登录)、“确认废弃”(离职人员残留);
  3. 处置:待认领的进入人工确认池;确认废弃的直接禁用并归档,处置动作全程留痕。

孤儿账号清理是账号共享治理里最容易被忽略、却最见成效的一环。一个常见的落地指标就是"孤儿账号占比"——归并前可能高达 15%~30%,治理后应压到个位数。

5.3 与多因素认证MFA的协同

唯一身份标识建立后,MFA 策略也能更精准。过去 MFA 是"按账号开",现在可以"按主体开":GPID 一旦被标记为高风险(如近期有异地登录、设备异常),所有挂在它名下的系统登录都强制二次认证,而不是只拦其中一个应用。身份归一让风险策略第一次有了"以人为单位"的抓手。

六、落地指标体系:怎么证明归并做对了

任何治理项目都要回答"凭什么说有效"。账号归并建议盯四类指标:

指标定义治理前典型值治理目标
账号重复率(账号总数 - GPID 数)/ 账号总数40%~60%< 10%
登录入口收敛度支持 SSO 的系统数 / 总系统数30%> 90%
审计覆盖率带 GPID 的审计事件 / 总审计事件偏低100%
孤儿账号占比孤儿账号 / 账号总数15%~30%< 5%

需要提醒的是,这些指标不是"达标即结束"。账号归并是一项持续运营:每天都有新员工入职、老员工调岗、离职人员清理,EMPI 要有增量匹配与定时体检机制。很多项目上线时指标漂亮,半年后重复率又反弹,根因就是只做了一次性归并、没有持续运营。

以安当ASP为例,它支持的 LDAP / RADIUS 协议正好对应企业里最难归一的两类账号源——目录类与服务接入类。把这两类账号也纳入 account_link 映射后,统一身份认证平台在 SSO 时就能把网络设备登录、服务器登录、OA 审批全部归因到 GPID,等保2.0三级要求的"审计记录应包括事件的日期和时间、用户、事件类型"才真正能串成一条完整的自然人责任链,而不是散落在网管日志、应用日志里的碎片。

七、一个市级民政局的真实缩影

某市级民政局此前有 11 套业务系统,各自一套账号体系,员工办事要记 11 套密码,登录平均耗时约 3 分钟。更关键的是审计各自为政,无法对一个经办人跨系统的操作做整体责任认定。

在引入 EMPI 与统一认证平台后,他们做了几件事:先以工号为强键、证件号 SM3 哈希为次键,把 11 套系统的账号全部建立 account_link;再对少量历史遗留的重名账号走人工确认兜底;最后让 SSO 统一签发 GPID 会话,并做全量历史日志回填。

结果是:登录入口收敛到单点,平均登录时间从 3 分钟降到 10 秒左右;跨系统行为第一次能归因到同一自然人,审计覆盖率达到完整;孤儿账号从几十个清理到个位数。这个项目里没有增加任何新业务功能,纯粹是把"人"这一层主数据理顺,收益却直接体现在效率与合规两条线上。

八、常见踩坑与规避

  1. 只建 EMPI 不动账号:以为有了唯一标识就万事大吉,结果各系统本地账号还是各用各的,GPID 成了"另一张表",没有下沉到 SSO 解析环节。EMPI 必须接入认证链路才产生价值。
  2. 明文存证件号:为图匹配方便把身份证号明文入库,反而制造合规雷点。务必 SM3 摘要后存储与比对。
  3. 阈值拍脑袋:概率匹配阈值不抽样校验就上线,导致大量误归并或漏归并。上线前必须离线跑全量 + 人工抽样。
  4. 不回填历史日志:只对新事件打 GPID,历史审计断层,责任溯源断裂。历史日志回填是连续性的前提。
  5. 归并不留痕:主体合并、账号 reparent 不写审计,事后查不清"为什么这两个人是一个"。合并动作必须签名留痕。
  6. 一次性运动式治理:上线即终点,半年后重复率反弹。EMPI 需要增量匹配与定时体检的持续运营机制。

方案参考

账号归并与唯一身份标识(One Identity)的本质,是主数据治理在身份领域的具体落地,并不依赖某个特定产品。任何组织在推进企业身份管理现代化时,可参考以下通用方法论:

  • 先立唯一主体,再做统一认证:在接入单点登录SSO之前,先建立以 GPID 为核心的 EMPI,把"人"这个主体从散落的本地账号中抽象出来,避免 SSO 只是把多个入口叠在一起。
  • 强键优先,弱键兜底,人工收口:匹配规则先用工号、证件号哈希、主邮箱这类强键做确定性归并,再用加权概率匹配处理弱键场景,最后对灰色区间与冲突案例一律人工确认,任何归并都要留痕可溯源。
  • 敏感键国密摘要存储:证件号等个人敏感信息只存 SM3 哈希,匹配时同算法摘要后比对,兼顾精确归并与等保2.0对敏感信息加密存储的要求。
  • 审计事件统一带 GPID:所有系统的认证、操作、审批事件归因到同一自然人,并对历史日志做回填,确保跨系统责任溯源连续不断层。
  • 身份随 HR 状态流动:离职、调岗等状态变化应级联禁用或重新映射各源系统账号,杜绝权限冗余与影子账号,这是账号共享治理的底线。
  • 持续运营而非一次性项目:建立增量匹配与定时体检机制,持续监控账号重复率、登录入口收敛度、审计覆盖率、孤儿账号占比四项指标,防止治理成果反弹。

以上方法论与具体实现无关,是身份主数据治理的通用骨架。真正的难点从来不是协议选型或工具引入,而是把散落在 HR、AD、LDAP、OA、RADIUS 与各业务系统里的"同名异构账号"重新认领回同一个自然人,并让这条唯一身份标识持续、准确地运转下去。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询