ARTICLE DETAIL

资讯详情

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

企业微信接入Hadess实现统一认证登录的完整实践

企业微信接入Hadess实现统一认证登录的完整实践 做过内部系统账号体系的人大概率都经历过这种场面OA一套密码、GitLab一套密码、运维平台又要单独维护一套账号员工记不住就天天找回密码IT这边光是处理账号重置就占掉不少工单。我们当时决定把所有系统的认证收拢到一套统一认证服务上内部代号就叫Hadess。第一个要接的认证源选的就是企业微信。原因很直接全员都在用组织架构现成而且企业微信自带扫码登录和OAuth2.0授权能力落地统一认证的路径最短。这篇文章把我们在Hadess里接入企业微信、实现统一认证登录的完整过程摊开讲一讲包括企业微信后台怎么配、Hadess里认证源参数怎么填、授权码模式到底跑通了哪些步骤、用户绑定怎么做、以及实际联调中遇到的报错和排查方法。内容偏向实操适合正在做内部账号体系整合、或者准备把企业微信作为SSO入口的开发和运维同学参考。1. 为什么选择Hadess做统一认证登录1.1 统一认证要解决的三个真实问题先说回场景。公司内部系统一多账号体系自然就散掉了。最头疼的问题有三个。第一个是密码管理成本。每个系统都有自己的密码策略有的要求三个月一换有的要求大小写加特殊字符员工根本记不住最终都会变成“找回密码”工单。第二个是账号生命周期不一致。员工离职后如果不逐个系统清理就会留下大量僵尸账号这是安全隐患里非常容易被忽略的一块。第三个是访问审计缺失。登录分散在各系统本地没有一个统一的地方追溯“谁在什么时间登录了什么系统”等真正需要追责或排查安全事件的时候只能各个系统后台翻日志效率极低。统一认证的价值就是把“认证”这个动作从各业务系统里剥离出来集中到一个地方完成。用户只需要在入口登录一次后续访问其他系统时由统一认证平台负责签发身份凭证业务系统只需校验这个凭证是否有效不必再各自维护一套密码。1.2 Hadess在认证体系里扮演的角色Hadess在我们内部定位是统一身份认证网关核心能力有三块管理多种认证源、签发和校验会话凭证、维护本地用户与外部身份源的映射关系。认证源这一块它可以接企业微信、钉钉、飞书、LDAP、以及普通账号密码登录。实际使用中企业微信是办公场景下主推的入口因为员工在PC端经常挂着企业微信移动端也在用扫码或者点击授权就能完成登录比输入账号密码自然很多。协议层主要走OAuth2.0和OIDC标准。业务系统接入Hadess时不需要关心背后到底是企业微信还是LDAP只需要按照标准协议把用户重定向到Hadess的登录地址登录完成后拿到一个授权码再用授权码换取身份信息最后校验ID Token或者调用用户信息接口拿到用户资料。这种分层让Hadess变成了一个中间层底层认证源可以随时切换、增加上层业务系统完全无感。本地用户映射是统一认证里最需要花心思的部分。企业微信返回的是useridGitLab、Jira、内部运维平台各自有自己的一套用户标识。Hadess在中间做一层映射企业微信的userid对应本地用户表的external_id本地用户表再关联业务系统需要的登录名和邮箱。这样既保证来源可追溯也能兼容旧系统已有的账号。1.3 企业微信作为首选认证源的原因选企业微信作为第一个接入的认证源不是因为它技术最复杂反而是因为它“足够简单直接”。第一覆盖率高。公司全员都在企业微信通讯录里不需要额外导入用户认证源天然自带组织架构。第二免密体验好。企业微信提供两种常见方式一种是在PC端的企业微信内打开应用直接免授权登录一种是扫码授权登录。无论哪种用户都不需要再输入一套新密码。第三身份信息可靠。企业微信的用户信息里有userid、姓名、邮箱、手机号等字段而且手机号默认加密返回至少能保证身份不会凭空捏造。当然企业微信也不是没有坑。比如自建应用的回调域名要求一级域名已备案本地开发环境处理回调地址会比较别扭又比如用户信息接口需要单独的Secret权限权限没开够就会报错。这些细节放在后面的实操部分展开。2. 企业微信侧准备自建应用与参数获取2.1 前置条件与管理权限在开始配置前先确认三件事。第一你必须是企业微信的超级管理员或者被授权了“应用管理”和“通讯录”相关权限。如果不是管理员连自建应用入口都看不到也就拿不到AgentId和Secret。第二需要有一个已备案并配置了HTTPS证书的域名。企业微信授权登录的回调地址必须在这个域名下而且域名需要配置可信域名。如果公司没有现成的域名准备一个SSO专用的子域名比如 sso.公司域名.com会更干净。第三需要决定回调地址走什么路径。这个路径是暴露在Hadess公网入口上的建议单独规划。2.2 创建自建应用的五个关键步骤登录企业微信管理后台进入“应用管理-应用-自建”点“创建应用”。这里有几个步骤容易漏我按顺序列一下。第一步填写应用基础信息。应用名称建议直接叫“统一认证”或者“SSO”方便员工识别。应用Logo可以随便传一个不影响技术对接。第二步设置可见范围。这一步很关键。可见范围决定哪些成员能在企业微信里看到这个应用、能通过这个应用登录。建议先配一个测试部门或者少量测试成员等联调完成后再扩大到全员。如果一开始就把全员勾上联调期间出问题会影响所有人。第三步配置“网页授权及JS-SDK”的可信域名。需要在“企业微信后台-应用管理-自建应用-应用详情-开发者接口”里找到网页授权及JS-SDK填写可信域名。这个域名必须与后续回调地址的域名一致而且要按企业微信的要求把校验文件上传到域名根目录或者通过API方式完成域名校验。第四步记下三个核心参数企业IDCorpId、AgentId、Secret。企业ID在“我的企业-企业信息”里查看AgentId和Secret在应用详情页里查看。Secret会在创建应用或者重置后只显示一次务必保存好。第五步配置回调地址。这里要区分两种情况如果只是做网页扫码登录和OAuth授权不需要配置“接收消息”的回调URL如果后续需要同步通讯录、接收成员变更事件才需要在“接收消息”里填一个回调URL并配置Token和EncodingAESKey。我们第一阶段只做认证登录所以跳过消息回调配置。2.3 回调地址与可信域名的区别很多第一次接企业微信的同学会把“可信域名”和“回调地址”搞混这里单独说一下。可信域名是用于OAuth授权时校验重定向地址合法性的一组域名。企业微信在发起授权时会对redirect_uri的域名做校验如果不在可信域名列表里会直接提示“redirect_uri非法”。所以可信域名必须配置且必须与最终回调URL的域名完全一致包含端口也不行。回调地址则是实际接收授权回调的完整URL路径比如 https://sso.example.com/api/v1/auth/callback/wecom 。这个路径由Hadess暴露出来企业微信授权完成后会把用户浏览器重定向到该地址并携带code参数。简单总结可信域名负责“允许这个域名下发起授权”回调地址负责“授权完成后的实际落点”。配置时先确保域名匹配再确保路径可达。2.4 获取企业微信侧必要参数的完整清单在进入Hadess配置前建议先整理一份参数清单避免配置到一半回去翻后台。我通常用下面这个表格记录。参数名称获取位置用途CorpId我的企业-企业信息企业唯一标识OAuth请求和获取access_token都需要AgentId应用详情页自建应用ID部分接口需要尤其是获取用户信息Secret应用详情页调用企业微信API的凭证与AgentId对应可信域名应用详情-开发者接口回调地址域名校验回调地址自己规划Hadess接收授权回调的完整URL参数准备好后就可以去Hadess里添加认证源了。3. Hadess认证源配置与核心参数3.1 在Hadess里添加企业微信认证源Hadess管理端登录后在“认证源管理”页面新建一个认证源类型选择“企业微信”。这里需要填写的内容和企业微信后台的参数一一对应。配置界面上主要分三块基础信息、协议参数、字段映射。基础信息包括认证源名称、是否启用、是否作为默认登录方式。协议参数包括CorpId、AgentId、Secret、授权地址、Token地址、用户信息地址、回调地址、授权scope。字段映射用于把企业微信返回的用户字段关联到Hadess本地用户模型。我贴一份实际可用的配置样例YAML格式方便直接理解对应关系。auth-sources: wecom: type: wecom enabled: true default-login: true corp-id: ww1234567890abcdef agent-id: 1000002 corp-secret: your-corps-secret authorize-url: https://open.weixin.qq.com/connect/oauth2/authorize token-url: https://qyapi.weixin.qq.com/cgi-bin/gettoken user-info-url: https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo redirect-url: https://sso.example.com/api/v1/auth/callback/wecom scope: snsapi_privateinfo field-mapping: external-id: userid display-name: name email: email mobile: mobile注意这里的企业微信类型配置并没有走标准的OIDC discovery因为企业微信的OAuth接口不是完全标准的OIDC端点。所以需要我们手动指定授权URL、Token URL和用户信息URLHadess再按OAuth2.0授权码模式封装成统一协议。3.2 OAuth2.0授权码模式在企业微信场景下的应用企业微信网页登录走的是OAuth2.0的授权码模式流程在外人看来可能就是“点一下授权”实际上后台发生了四步。第一步前端跳转到授权地址。Hadess在检测到用户未登录后会生成一个跳转链接指向企业微信授权地址同时带上参数appidCorpId、redirect_uriHadess回调地址、response_typecode、scopesnsapi_privateinfo、state随机字符串。第二步用户在企业微信的授权页确认身份。如果用户在企业微信客户端内打开且可信域名校验通过这个过程几乎是无感的直接跳转回回调地址并携带一个临时code。第三步Hadess拿到code后先调用企业微信接口获取access_token然后使用access_token和code调用auth/getuserinfo接口换取userid。这一步需要先取access_token的原因是企业微信要求身份接口必须使用应用级access_token临时code是一次性的有效期只有五分钟。第四步Hadess根据userid查本地映射关系找到或创建本地用户签发Hadess自己的登录态再重定向回业务系统的原始访问地址。这个过程中业务系统全程不感知企业微信的存在它只知道Hadess是OAuth2.0的授权服务器。3.3 字段映射与用户绑定逻辑字段映射是接入过程中最需要想清楚的地方。企业微信返回的核心字段有userid、name、avatar、email部分情况下还有mobile和gender。但企业微信的mobile字段默认是加密的需要申请权限才可能拿到明文email字段也可能因为成员未填写而缺失。所以我在做映射时首要标识只用userid这个字段绝对不会重复。Hadess在第一次收到某userid的登录请求时会执行“查找-创建-绑定”的逻辑。先按external_iduserid查本地用户表查到就直接登录查不到再尝试按照email匹配已有账号如果只有一个账号匹配就自动绑定到这个账号上如果email也没匹配到就创建一个新账号display_name用企业微信的name状态标记为“来源于企业微信”。这种策略的好处是老系统账号可以平滑迁移。比如某员工原本在GitLab里使用邮箱作为登录名Hadess可以通过email把企业微信账号和他已有的GitLab账号关联起来避免生成一个全新账号。不过自动绑定有条件只有在email匹配且唯一时才会自动完成。如果同一个email对应多个本地账号Hadess会选择不自动绑定而是跳到管理员审核页面避免绑错账号造成权限错乱。3.4 与既有账号体系的兼容处理如果公司已经有了一套账号体系直接全部切换到企业微信登录有风险尤其是那些从上下游合作方来的账号可能根本没有企业微信身份。我们当时的做法是保留多种认证源共存。企业微信作为首选登录方式密码登录仍然保留但标记为“备用登录”。Hadess在登录页上优先展示企业微信入口同时把“密码登录”折叠起来这样大多数员工走扫码或免登少数特殊账号继续用密码。从长期运维角度看密码登录不会立刻下线。等企业微信绑定率达到95%以上再考虑逐步关闭密码登录入口。这样既保证过渡平稳也避免一刀切带来的工单爆炸。4. 统一认证登录完整流程拆解4.1 用户从访问到免登的六步时序完整流程可以拆成六步方便排查问题时定位。第一步用户访问业务系统。比如打开公司内部的GitLabGitLab发现没有登录态重定向到Hadess的登录地址并带上回调地址。第二步Hadess判断用户当前没有SSO会话且默认登录方式是企业微信于是重定向到企业微信授权地址。第三步用户在企业微信侧确认身份。若在企业微信客户端内表现为自动跳转若在浏览器中则出现扫码页面。第四步企业微信回调Hadess携带临时code和state。Hadess验证state与发起时一致然后通过code换取userid。第五步Hadess完成本地用户匹配与会话创建生成登录票据再重定向到业务系统最初的回调地址并附带授权码或ID Token。第六步业务系统拿授权码向Hadess换取用户信息建立自己的会话。之后用户再访问其他已经接入Hadess的系统时由于Hadess的SSO会话已经存在会直接完成登录不再重复出现企业微信授权页。4.2 会话票据与Token生命周期Hadess里有两层会话需要区分。一层是Hadess自身的SSO会话通常用Cookie保存有效期建议设置为8小时到24小时。这个会话是统一认证的核心。用户只要这个会话没过期访问任何接入系统都无需再次输入密码。另一层是发给业务系统的Token。这里推荐使用JWT形式的ID Token并设置较短的过期时间比如30分钟到2小时。业务系统在拿到ID Token后可以解析里面的用户标识、姓名、邮箱等信息用于本地会话建立。JWT需要由Hadess用私钥签名业务系统用公钥验签。如果做得更规范Hadess可以提供OIDC discovery端点业务系统可以自动获取公钥也就是jwks端点。这一层做好后业务系统的接入成本会很低。4.3 登出行为如何做到多系统同步单点登录爽单点登出往往被忽略但员工在公用电脑上如果没登出后续隐患很大。Hadess的登出流程是用户点击某业务系统里的“退出登录”该系统先清除自己的会话然后重定向到Hadess的登出地址。Hadess清除自己的SSO会话后再按接入系统列表逐个通知或者通过前端跳转到各系统的登出URL让所有业务系统的本地会话也一并失效。由于各业务系统技术栈不一致我们采用的是“Hadess统一发起跳转通知”的方式。每个接入系统在注册时需要提供一个logout URLHadess在登出时用隐藏iframe或顺序跳转的方式调用这些地址。如果系统不支持远程登出至少要保证Hadess侧会话失效这样下次访问时会强制重新认证原授权码和Token也会因为后端校验不通过而无法使用。4.4 安全细节state校验和回调重放防护对接OAuth最容易被忽略的是state参数。state是发起授权时生成的一个随机字符串存在用户浏览器会话中。企业微信回调时会原样带回这个stateHadess需要校验回调中的state与发起时的一致否则拒绝处理。这个机制能防止跨站请求伪造攻击。另外临时code是一次性的Hadess在换取userid后应当在本地记录该code已使用。如果同一个code再次请求用户信息接口企业微信侧会返回失效但为了稳妥Hadess还应记录本地的消费状态避免攻击者利用历史回调URL重放。还有一个细节回调地址的域名一定要走HTTPS防止code在传输过程中被截获。这个在前期可信域名校验时就已经限制了但本地测试时容易图省事用HTTP正式环境千万不要这么干。5. Hadess核心模块实操从搭建到联调5.1 用容器快速搭建Hadess开发环境如果从零开始搭环境最省事的方式是用容器跑Hadess。项目仓库里提供了docker-compose文件核心包含三个服务数据库、缓存和Hadess本体。数据库我们用的PostgreSQL缓存用的Redis。Not necessary? compose里默认带了一个PostgreSQL和Redis先把这两个启动再启动Hadess服务。git clone https://your-gitlab/hadess/hadess.git cd hadess cp .env.example .env # 修改 .env 里的数据库连接、Redis 连接、JWT 密钥 docker-compose up -d启动完成后访问管理端地址默认端口可以做成8080。第一次进入管理端需要初始化管理员账号走完初始化后就能看到“认证源管理”的菜单。5.2 企业微信认证源配置样例在管理界面里添加认证源时各字段对应关系如下。corp-id填企业微信的企业IDagent-id填自建应用的AgentIdcorp-secret填应用Secret。redirect-url填Hadess暴露的回调地址例如 https://sso.example.com/api/v1/auth/callback/wecom 。scope固定填snsapi_privateinfo这个scope才能拿到userid对应的用户详情。字段映射部分external-id映射到useriddisplay-name映射到nameemail映射到email。mobile不建议直接映射因为加密问题会导致大部分用户拿不到绑定时容易失败。保存后Hadess会立即生成一条config记录。此时可以先用管理端提供的“测试登录”按钮发起一次真实的企业微信授权流程看能否完整跑通。5.3 验证认证流程的接口与前端调试要点联调时最常用的是管理端的“测试认证源”功能。点击后Hadess会模拟前端发起授权跳转等授权完成回跳后显示完整的用户信息JSON。如果管理端没有这个功能也可以通过curl手动模拟关键接口。先构造授权跳转地址https://open.weixin.qq.com/connect/oauth2/authorize?appidww1234567890abcdefredirect_urihttps%3A%2F%2Fsso.example.com%2Fapi%2Fv1%2Fauth%2Fcallback%2Fwecomresponse_typecodescopesnsapi_privateinfostatetest123#wechat_redirect用浏览器打开这个地址完成授权后会跳转到回调地址并带回来code和state。拿着code先获取应用级access_tokencurl https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpidww1234567890abcdefcorpsecretyour-corps-secret再用access_token和code获取useridcurl https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_tokenACCESS_TOKENcodeCODE正常响应里会包含userid配合name、email等字段就完成了身份确认。前端登录页面的集成要点在跳转URL的拼接。建议在Hadess提供的登录链接里带上return_url参数这样用户登录完成后能准确回到最初想访问的页面而不是固定跳转到系统首页。5.4 在本地环境联调企业微信回调的可行方案本地开发环境最大的痛点是回调地址必须是公网HTTPS域名而本地一般是localhost。我们当时的方案是准备一个用于联调的测试域名比如 dev-sso.公司域名.com通过内网穿透工具映射到本地8080端口。这个域名要提前在企业微信可信域名里配置好并配置HTTPS证书。另一种做法是在云服务器上临时部署一个Hadess测试实例回调域名直接用测试服务器域名。联调通过后再把配置切到生产。这个方式更稳因为内网穿透偶尔会不稳定企业微信回调如果因为穿透服务断掉而失败排查起来会多一层干扰。总之本地联调的关键是先解决域名可达性再谈其他。6. 常见问题与排查技巧实录6.1 redirect_uri非法与地址参数丢失最常见的是“redirect_uri参数错误”或者“redirect_uri非法”。这类问题九成是可信域名没有配置或者域名与回调地址不一致。企业微信对域名的校验只看协议、域名和端口。比如可信域名填的是“sso.example.com”但回调地址写的是“https://sso.example.com:8443/...”带端口会直接非法。另外redirect_uri在拼接时需要做URL编码如果Hadess返回的授权地址里redirect_uri没有编码也会报同样的问题。排查时可以先用浏览器的开发者工具看实际跳转的企业微信地址检查redirect_uri参数是否是完整的URL编码值。如果编码后把符号搞丢了回调地址就会被截断。6.2 企业微信返回errcode 40013或40014errcode 40013表示corpid无效40014表示access_token无效。40013出现时检查corp-id有没有填错尤其是容易把企业微信的应用ID“ww”前缀看漏。40014出现时大概率是使用了同一个Secret的access_token去调用了另一个应用的接口。企业微信的access_token是按应用的AgentId和Secret维度生成的不同应用之间不能混用。Hadess配置里要确保agent-id和corp-secret属于同一个应用。还有另一种情况access_token有效期内重复获取旧的会失效但代码里如果没做缓存每次调用用户信息接口都重新获取短时间多次获取后企业微信会踢掉旧的token。Hadess里建议对access_token做缓存至少缓存到过期前五分钟。6.3 用户信息获取不到手机号或邮箱手机号默认加密返回如果应用没有申请“手机号”权限auth/getuserinfo接口返回的mobile会是空的。企业微信需要单独申请敏感字段权限一般是提交说明材料管理员审核通过后才有权限。邮箱字段也经常缺失因为企业微信通讯录里很多成员不维护邮箱。所以字段映射时不要依赖这两个字段作为唯一绑定依据。我们的策略是userid作为主绑定键email只在匹配已有账号时使用匹配不到就自动创建新账号而不是报错中断。6.4 登录成功后反复掉线会话不一致这类问题多出现在从SSO登录到业务系统建立会话的环节。业务系统拿ID Token换用户信息时如果缓存了公钥但不更新而Hadess轮换了JWT签名密钥旧公钥验签就会失败表现为用户明明登录成功访问业务系统却被踢回登录页。解决方法是让业务系统每次验签失败时主动刷新一次Hadess的jwks端点再重试验签。如果业务系统使用共享密钥校验确保密钥更新后同步修改了双方配置。另外业务系统自身的会话过期时间不要比Hadess的SSO会话长。业务系统会话过短会频繁要求重新认证而过长会导致员工离开很久后依然自动登录安全上不划算。建议业务系统本地会话时长设置为30分钟到1小时Hadess SSO会话设置为8小时。6.5 配置生效慢或缓存问题Hadess认证源配置更新后个别环境出现“测试登录时还是使用旧参数”的现象。这是因为配置缓存没有主动刷新。多数情况下等缓存过期即可但为了减少等待可以在管理端手动触发一次配置缓存刷新或者重启Hadess核心服务。还有一种隐藏情况企业微信侧修改了可信域名但旧域名还在列表里新域名配置后没有重新“校验”导致仍然使用旧域名。进入企业微信后台在可信域名管理里重新校验一次再测试。6.6 排查小工具日志、抓包与OAuth调试器排查OAuth类问题我通常会按三条线同时看。第一看Hadess的系统日志。日志里会记录每次授权请求的来源、回调地址、state、以及调用企业微信接口的返回码。大部分问题能在日志里直接定位。第二浏览器开发者工具看Network面板。重点观察两个请求跳转到企业微信授权地址的请求参数以及回调Hadess的请求参数。如果code没带回来说明授权地址参数有问题如果code有但后续接口报错则问题在Token或用户信息接口。第三准备一个OAuth调试工具。这不一定需要额外软件直接在浏览器里手动拼一个授权地址用curl模拟Token请求和用户信息请求就能判断是企业微信侧的问题还是Hadess侧的问题。最后再分享一个小技巧联调前先把企业微信后台的“开发者接口”里的接口权限过一遍。权限决定接口能返回哪些字段权限不够时很多接口能调通但字段是空的那种问题最迷惑人。我实际踩过几次坑之后已经习惯在项目开始前先整理一份权限清单交给管理员统一开通省得联调到一半才发现某个字段拿不到。
返回列表