自从接手过一家同时上了泛微E9和金蝶云星空的制造型企业项目后,我算是彻底明白了什么叫“快乐是一套系统的事,账号是两套系统的事”。业务部门每天都在OA里审批流程,又得切到金蝶做单据,经常有人忘记密码、被锁账号,IT部门一个月光重置密码就得花掉大半天。后来我把泛微E9和金蝶云星空的单点登录集成做通了,登录体验才真正像一家公司的系统。
这篇文章就完整复盘一下这次集成项目的全过程:从方案选型、两边认证机制拆解,到具体配置步骤、插件编写、常见坑点处理,全部覆盖。看完之后,哪怕你之前完全没碰过这两套系统的集成,也应该知道该从哪里下手、每一步怎么做、遇到问题怎么查。适合正在做泛微E9和金蝶云星空对接的架构师、实施顾问、后端开发,也适合被“账密不统一”困扰的IT管理人员先拿来当参考。
1. 集成前的需求拆解与方案选型
1.1 为什么要做单点登录:双系统的账户之痛
我遇到的这个客户,泛微E9负责办公协同、工作流审批、行政事项,金蝶云星空负责财务核算、供应链、生产制造,两边人数重叠度很高。员工每天的工作流基本都是“OA提申请、金蝶做业务、再回OA看审批结果”,账户来回切换的频率非常高。
痛点很直白:
- 账号规则不一致。泛微E9里习惯用工号做登录名,金蝶云星空里可能用的是手机号或邮箱,两边根本不是同一个字符串。
- 密码策略不统一。OA要求8位以上字母数字组合,金蝶要求12位以上且必须包含特殊字符,还要90天强制改密,很多人改完没几天又忘了。
- 管理员是救火队员。用户密码过期、锁定、遗忘,业务人员不会找系统管理员,直接找IT部门,一天下来全是重复劳动。
- 共享账号偷偷存在。有人嫌切换麻烦,直接共用一套有权限的金蝶账号,这给后续审计和权限管控埋了很大的雷。
所以客户提出来的需求就一句话:在OA里点一下,就能直接进金蝶,不要再让我输密码。这句话翻译成技术需求,就是单点登录,也就是常说的SSO。但SSO不是单独一个功能,必须把账号体系和登录会话打通,才能实现“一次登录、处处访问”。
1.2 三条技术路线的对比与选型
做集成方案之前,我根据这个项目的现状列了三个可选方向,也建议正在做选型的朋友先这样梳理一遍:
| 方案路线 | 建设成本 | 改造范围 | 适用场景 |
|---|---|---|---|
| A:以泛微E9为统一入口,OAuth2 + 金蝶登录插件 | 中 | 金蝶侧需要二次开发 | 企业没有独立AD/LDAP,OA是日常最高频入口 |
| B:引入LDAP/AD统一用户认证,金蝶和OA都对接LDAP | 高 | 需要搭建或梳理目录服务 | 企业已有AD域,且希望统一管理全账号 |
| C:企业微信/钉钉作为身份源,两套系统分别对接 | 中 | 需要三方应用配置和用户绑定 | 移动办公渗透率高,日常使用企微/钉钉 |
这个客户当时没有成熟的AD域,也没有专职目录服务团队,所以LDAP方案虽然长期看最规范,落地成本和周期都不合适。企业微信虽然也在用,但业务方明确表示希望网页端登录体验优先,移动端可以后续再补充。最后我的选择是方案A:泛微E9作为身份认证中心,金蝶云星空通过自定义登录插件接收OA传递的票据,完成免密登录。
这个选择的核心逻辑是:OA和使用者的日常接触频率最高,把认证入口放在高频系统,用户感知最好;金蝶侧的改造也只需要聚焦在一个登录认证插件上,不容易影响ERP核心业务。
2. 核心细节解析:两边的认证机制
2.1 泛微E9的OAuth2认证能力
泛微E9本身带有OAuth2服务端的支持,这是做集成最省事的地方。它允许我们按照OAuth2的标准授权码模式,把用户认证和授权动作交给泛微来完成。
生产中我用的流程是这样的:
- 用户在OA侧已登录,点击菜单需要跳转金蝶。
- 前端请求泛微的授权端点,地址大概是 /oauth2/authorize 这类路径,参数带上 client_id、redirect_uri、response_type=code。
- 用户会话在OA里有效,泛微自动放行,然后302重定向到金蝶的回调地址,并携带一个一次性授权码 code。
- 金蝶侧用 code 向泛微换取 access_token。
- 拿着 access_token 去请求用户信息接口,拿到用户在OA里的登录名、姓名、部门等基础信息。
不同版本的E9路径前缀会有差异,比如有的部署在 /api/ec/dev/auth 下,有的集成中心里叫“OAuth2服务”,所以我不建议死记硬背地址,而是在后台找“第三方应用”或“集成认证”相关菜单,里面能看到确切的端点信息。
关键技术点在于 redirect_uri 必须和后台注册时填写的回调地址完全一致。我见过不少集成失败案例,排查到最后发现就是回调地址多了一个斜杠或者大小写不一致,OAuth2授权服务直接拒绝。另外,client_secret 一定不能出现在前端代码里,换 code 的请求必须在后端服务器完成。
2.2 金蝶云星空的第三方认证插件机制
金蝶云星空虽然不像泛微那样天然提供标准的OAuth2服务端,但它给开发人员留了一个很好的扩展点:登录认证插件。这个插件基于金蝶BOS平台,允许我们在登录环节插入自定义逻辑。
金蝶的登录流程大致是这样的:浏览器访问登录页,用户输入账号密码后,请求到服务端认证逻辑。认证逻辑本身是一个可以扩展的管道,我们通过继承登录插件基类,重写相关方法,就能把“账密校验”替换成“外部票据校验”。
我当时实现的核心是写了一个 LoginServicePlugIn 的扩展类,在 CheckLogin 方法中做了两件事:
- 判断请求参数里是否存在 OA 传过来的 ticket 票据。
- 如果存在,就调用泛微的票据验证接口,确认票据合法后,从票据里解析出用户账号,并把这个账号交给金蝶登录上下文,直接完成登录,不需要金蝶侧再校验密码。
这个机制的好处是把复杂的OAuth2处理和用户信息映射都隔离在金蝶登录插件内部,ERP本身的业务逻辑完全不需要动。坏处是金蝶BOS插件属于二次开发,对部署和版本管理有要求:DLL文件要放进金蝶站点 Bin 目录,登录插件要在后台完成注册,还要清缓存、重启应用池。
2.3 票据设计与安全性考虑
泛微和金蝶之间传递的登录凭证,我建议不要直接用OAuth2的 access_token,而是做一层“一次性票据”中转。
原因是OAuth2的 access_token 有效期通常比较长,如果直接出现在金蝶登录URL上,一旦被浏览器历史记录、反向代理日志或者WAF抓到,等于把登录凭证暴露给了第三方。所以我当时在OA后端增加了一个票据签发接口,流程变成了:
- OA后端确认用户登录态有效后,生成一段短时效票据,里面包含 loginName、displayName、timestamp、nonce。
- 用约定好的签名密钥对票据做HMAC-SHA256签名。
- 签名和票据一起拼到金蝶登录地址里,例如 https://金蝶域名/K3Cloud/Login.aspx?ticket=xxx&sign=yyy。
- 金蝶插件先用签名密钥重新计算一遍签名,一致后再校验时间戳和 nonce,全部通过才调用泛微接口换用户信息。
票据有效期我设成了5分钟,nonce 在金蝶侧做一次内存缓存,避免票据重放攻击。生产环境里我还用参数做了HTTPS强制说明,虽然内网部署很多客户觉得没必要,但只要涉及跨网段访问或者有远程办公场景,HTTPS还是强烈建议。
3. 实操过程:完整集成步骤
3.1 在泛微E9中注册第三方应用
第一步是在泛微E9后台把金蝶云星空注册为一个合法的第三方应用。我用的E9版本入口在“集成中心”或“开发平台”的第三方应用管理里,大致步骤如下:
- 登录E9系统管理员账号,找到集成中心/第三方应用注册菜单。
- 新建应用,应用名称随便填,比如“金蝶云星空SSO”。
- 填写回调地址,也就是金蝶登录页地址,必须精确到页面路径,例如 https://k3.xxx.com/K3Cloud/Login.aspx。
- 提交后系统会生成 client_id 和 client_secret,这两串值要保存好,后面换token要用。
- 配置授权范围,通常选择用户基础信息,也就是能拿到工号、姓名、部门这些字段。
整个过程本身并不复杂,有几个细节我特别提醒一下:
- 回调地址不要只填到根域名,一定要填到最终落地页面,否则tongueOAuth2的 redirect_uri 匹配会失败。
- client_secret 第一次展示后如果没保存,后续可能只能重置,所以拿到就先存密码库。
- 如果生产环境将来要换HTTPS域名,记得同步改这里,否则线上跳转直接报错。
这样注册完之后,泛微E9其实已经具备向金蝶提供认证服务的能力了,剩下的工作就是把金蝶这一侧接住。
3.2 编写金蝶云星空登录认证插件
金蝶侧这次的核心工作就是写一个自定义登录插件。我把核心代码简化成了下面这个结构,实际生产里大家可以根据自己的框架继续扩展:
using System; using Kingdee.BOS.ServicePlugIn; namespace Kindee.SSO { public class OALoginPlugin : AbstractLoginServicePlugIn { public override void CheckLogin(LoginServiceContext context) { // 1. 从请求参数中读取票据 string ticket = context.Request["ticket"]; string sign = context.Request["sign"]; // 2. 如果没有票据,走金蝶默认账密登录流程 if (string.IsNullOrEmpty(ticket) || string.IsNullOrEmpty(sign)) { base.CheckLogin(context); return; } // 3. 校验签名和票据有效性,获取OA用户信息 OaUserInfo user = OaTicketValidator.Validate(ticket, sign); if (user == null) { throw new Exception("OA票据验证失败,请返回OA重新登录"); } // 4. 根据OA登录名映射到金蝶账号 string k3Account = UserMapper.GetK3Account(user.LoginName); if (string.IsNullOrEmpty(k3Account)) { throw new Exception($"账号 {user.LoginName} 未做金蝶映射,请联系管理员"); } // 5. 设置金蝶登录上下文,跳过密码校验 context.UserId = k3Account; context.Password = Guid.NewGuid().ToString("N"); } } }这段代码的意图很明确:金蝶不是不校验身份了,而是把校验动作转移到了外部票据上。CheckLogin 里我们看到票据有效,就主动告诉金蝶框架“这个账号已经通过了身份核实”,密码字段随意填一个随机值,避免传入真实密码。
编译和部署有几个注意点:
- 插件项目要选择.NET Framework 4.x 版本,和金蝶云星空运行环境保持一致。
- 编译出的DLL放到金蝶网站的 Bin 目录,比如 K3Cloud/Website/Bin 下面。
- 管理后台需要启用登录认证插件,不同版本菜单名称可能叫“登录插件管理”或“第三方登录设置”。
- 部署完成后建议回收一次应用池,再清掉金蝶缓存,否则插件经常不生效。
我实际操作中,写完代码只花了一个上午,真正的调试倒是花了大半天,因为插件的加载、缓存、注册位置这些坑都是暗坑,不踩一遍很难意识到。
3.3 前端跳转与登录态保持
插件写好后,剩下的关键工作是把用户“送”到金蝶登录页,并在跳转时携带合法票据。
我当时的跳转方式是这样:
- 在泛微E9里配置一个菜单项,名叫“金蝶云星空”,菜单类型选URL。
- 菜单指向OA后端一个生成的中间接口,例如 /sso/kingdee/createTicket。
- 这个接口会校验当前OA用户是否已登录,如果未登录,先跳到OA登录页。
- 用户已登录,后端生成票据和签名,拼接金蝶登录地址,然后返回302重定向。
- 浏览器最终落到金蝶登录页,金蝶插件验证票据,完成免密登录进入首页。
这样的流程可以保证 ticket 永远不直接暴露在OA菜单配置里,而是每次用户点击时动态生成,过期和作废都非常灵活。
关于登录态保持,我强烈建议OA侧会话和金蝶侧会话的超时策略做一次对齐。很多客户反映“从OA跳转到金蝶后又提示登录超时”,其实不是SSO没打通,而是金蝶的会话有效时间比OA短,用户先在OA挂了半天,再点进金蝶时金蝶会话已经过期了。简单做法是把金蝶的会话超时时间适当调长,同时在OA会话快到期时提前提醒用户重新登录,避免一边有效一边失效的尴尬。
3.4 用户绑定与账号映射
这是整个项目里最不能跳过的一步,也是最容易低估工作量的一步。很多团队把OAuth2和插件都跑通了,结果上线第一天一半人进不去金蝶,原因就是金蝶账号与OA账号对不上。
我的做法是分三步走:
- 先导数据。从泛微E9导出所有在职用户的工号、姓名、手机号、邮箱,从金蝶云星空导出用户列表和员工编号。
- 再定映射规则。优先用手机号作为唯一键,因为手机号在两个系统里校验过唯一性;如果手机号不可靠,就用工号映射金蝶的员工编号。
- 最后补基础资料。在金蝶的员工基础资料里增加一个扩展字段“OA工号”,由集成脚本或人工维护,后续插件直接从映射表里查账号。
映射关系可以放在数据库表,也可以做成配置文件,但我建议放在数据库里,方便后期运维。我在生产里维护的是一张独立映射表,字段很简单:oa_account、k3_account、status、updated_time。
这里有个容易被忽略的问题:员工离职或转岗。如果员工在OA里已经离职,但是金蝶侧账号还在,映射表里应该及时标记失效,否则就会出现“OA账号已停用,但还能通过历史票据进入金蝶”的隐患。
4. 常见问题与排查技巧实录
4.1 登录报错“用户不存在”的几种原因
这个报错几乎每个做金蝶登录集成的人都会遇到。我总结下来大致有这几种情况:
- 映射表里查不到对应的金蝶账号,这是最常见的原因。
- 插件里设置了 context.UserId,但金蝶系统里该用户已禁用或离职。
- 账号映射到了金蝶的“用户”,但用户没关联到当前登录的组织,登录时也会报类似错误。
- 大小写不一致。金蝶账号如果区分大小写,而OA传过来的是小写工号,就会匹配失败。
- 插件配置在测试环境生效,但生产环境金蝶用户还没同步过去。
排查技巧是先看金蝶后台的登录日志,确认金蝶拿到的登录名到底是什么,再逐层对映射表、用户状态、组织关联。最好做一次用户连通性测试工具,直接输入OA账号,输出金蝶侧匹配结果,省得每次手工查表。
4.2 金蝶页面一直转圈或跳回登录页
页面一直转圈,一般不是前端问题,而是金蝶登录插件抛出异常后没有正确处理,默认走回了账密登录流程,而密码又是随机值,结果就是登录失败再跳回登录页,看起来像死循环。
这类问题的排查路径:
- 先看金蝶应用日志。插件异常一般会记录在 K3Cloud 的日志目录中,具体路径可以在管理后台的日志设置里查到。
- 检查DLL有没有真正部署到 Bin 目录,多个金蝶节点必须每个节点都拷贝。
- 检查插件注册信息是否指向了旧的类名或旧版本的DLL。
- 重试前先回收应用池、清一遍金蝶缓存。插件更新后不生效,八成是缓存问题。
我还遇到过一种隐蔽情况:金蝶前端做了验证码校验或滑块校验,而SSO请求直接到达登录服务器,被前端安全组件拦截。表现形式也是转圈或跳回,但日志里根本看不到插件执行记录。这种需要把SSO跳转地址加入金蝶白名单,或者走一次模拟登录动作。
4.3 会话过期与自动续期
账密登录的金蝶会话过期后,用户重新登录一次也就完事了,但SSO模式下会话过期会让人特别莫名,因为用户以为自己已经“登录过”了。
这里我建议做两个设计:
- 泛微侧的 access_token 用 refresh_token 做静默续期,用户一直活跃就不需要重新授权。
- 金蝶侧在页面检测到登录超时时,自动跳回OA的票据签发接口,而不是跳到金蝶原生登录页。由于用户OA会话可能还有效,后端很快能生成新票据,用户几乎感觉不到重新登录的过程。
另外还有一项容易被忽视的运维项:服务器时钟同步。票据校验里只要涉及时间戳,泛微服务器和金蝶服务器之间的时间偏差就不能太大。我遇到过客户私有化部署的两台服务器相差好几分钟,结果票据一到金蝶就提示过期。解决办法是把两台服务器都配置NTP校时,并轮询检查。
4.4 排查问题速查表
我把这次实操中最常遇见的几个问题整理成了一张速查表,群里排查问题时可以直接对照:
| 症状 | 可能原因 | 处理办法 |
|---|---|---|
| OAuth2授权页报redirect_uri不匹配 | 回调地址与后台注册不一致 | 核对协议、域名、端口、路径必须完全一致 |
| 用code换token报invalid_grant | 授权码一次性使用或已过期 | 让用户重新走授权流程,检查服务端时间 |
| 金蝶插件不触发 | DLL未部署或缓存未清理 | 拷贝DLL到所有金蝶节点,回收应用池、清缓存 |
| 跳转后URL没有ticket参数 | OA菜单URL配置错误或接口未生成票据 | 检查中间接口日志和菜单跳转地址 |
| 签名校验失败 | 两侧密钥不一致 | 核对HMAC密钥,注意字符编码 |
| 票据报时间戳超时 | 服务器时钟漂移 | 配置NTP同步,或临时放宽时间窗口 |
| 金蝶报用户无权限 | 用户未关联组织或角色异常 | 在金蝶后台检查用户组织、角色和业务范围 |
这张表不需要背,最好的用法是排查时按症状逐行对照,能少走很多弯路。
5. 集成后的运维与扩展思考
5.1 接入企业微信、钉钉等移动端入口
泛微E9和金蝶云星空都支持对接企业微信,很多企业最终希望实现的效果是:员工在企业微信里打开OA应用,点“金蝶云星空”菜单,直接进入金蝶,不要输账号也不要输密码。
这个需求本质上和网页端SSO一样,只是身份来源从“OA会话”换成了“企业微信身份”。泛微E9在企微集成后会建立一个员工身份关联,我们可以把“企微userId → OA工号 → 金蝶账号”这条链路串起来,实际对接时,只需要在跳转金蝶的接口里,通过企业微信身份反查出OA工号,再走一次票据签发逻辑即可。
这里最大的坑是用户映射不一致。金蝶云星空如果自己也对接了企业微信,而且使用了一套独立的企微映射,那么两套映射之间必须保持一致,否则就会出现“企微能打开OA,但跳金蝶时提示无权限”。我当时就把这个映射统一维护在了同一个表里,避免了多套映射互相打架。
5.2 工作流待办与消息通知的联动
单点登录只是解决了“能进去”的问题,接下来大家自然会追问:OA里的工作流待办,能不能直接跳到金蝶对应的单据?
这个需求我理解是SSO的自然延伸。泛微E9里有丰富的流程集成能力,金蝶云星空开放平台也提供了单据的访问地址。我做过的落地方式是这样的:
- 泛微工作流中,审批节点需要查看金蝶采购订单,流程表单里加一个“查看金蝶单据”按钮。
- 按钮点击时,OA后端生成一次性票据,拼上金蝶单据内码参数,跳转到金蝶对应单据页面。
- 金蝶侧同样走登录插件验证身份,然后跳转到指定单据,用户不需要先登录金蝶再找菜单。
这种做法把“进系统”和“看单据”合并成了一次动作,业务人员体验提升非常明显。需要注意的是,金蝶单据地址参数必须用内码而不是单据编号,内码的隐蔽性更强,且不受编号变更影响。
5.3 后续可能的增强方向
这套集成跑稳之后,哪些地方还可以继续强化?我根据自己的运维经验给几个方向:
- 审计与行为分析。记录每次SSO跳转、票据签发、金蝶免登的日志,汇总到统一日志平台,出现权限异常时可以回溯完整链路。
- 多因素认证。对管理员、财务人员这类高权限账号,在SSO基础上增加OTP二次验证,金蝶登录插件里可以插入OTP校验逻辑。
- 组织与角色同步。目前很多集成只做登录,不做组织架构同步。一旦两边的部门、岗位调整频繁,还是会产生权限错位,建议后续做按时定向同步。
- 备用登录通道。SSO链路偶尔也会故障,要保留一条紧急账密登录开关,并做好权限隔离,避免SSO故障时全员无法作业。
这些扩展方向不需要一次性做完,按企业实际需要排优先级,我个人的建议是把审计日志和联动单据的事情放在最前面,因为它们直接影响线上问题的追溯效率和日常使用体验。
最后说一个我在这个项目里最大的感悟:单点登录的技术难点其实不在“登录”,而在“账号底数”。OAuth2配置、插件代码、跳转逻辑,这些都有文档可以参考,反而是两边账号怎么对齐、怎么保证映射长期准确、怎么应对人员变动,才是真正决定这个项目稳不稳定的关键。如果正准备做泛微E9和金蝶云星空的SSO,我建议你第一步不是碰代码,而是先把两边用户数据拉出来,认认真真做一遍账号清洗。这个工作越早做,后面就越省心。