src/views/_core/authentication/login.vue)src/views/_core/authentication/social-login.vue)src/views/_core/authentication/qrcode-login.vue)| 变更 | 影响前端 |
|---|---|
| 企业微信首次扫码**自动注册**账号,不再报"未绑定账号" | 是 —— 登录页需新增企业微信登录入口,并去掉首次登录的绑定弹窗分支 |
| 登录成功后向用户企业微信推送**登录通知** | 否 —— 后端异步发送,前端无感知 |
| 新增企业微信**回调接口**(URL 验证 + 消息接收) | 否 —— 企业微信服务器调用,与前端无关 |
| 方法 | 路径 | 说明 | 前端是否调用 |
|---|---|---|---|
| GET | /admin-api/system/auth/social-auth-redirect |
获取企业微信授权跳转地址 | 是 |
| POST | /admin-api/system/auth/social-login |
使用授权 code 换取登录令牌 | 是 |
| POST | /admin-api/system/auth/login |
账号密码登录(可附带社交绑定参数) | 是(保留) |
| GET | /admin-api/wxcp/callback |
企业微信回调 URL 验证 | 否 |
| POST | /admin-api/wxcp/callback |
企业微信消息/事件接收 | 否 |
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | Integer | 是 | 社交类型,企业微信固定传 30 |
| redirectUri | String | 是 | 授权回调地址,即前端 social-login 页面完整地址 |
响应:
{
"code": 0,
"data": "https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid=xxx&agentid=xxx&redirect_uri=xxx&state=xxx"
}
前端拿到 data 后直接跳转该地址,用户扫码确认后企业微信会带 code 和 state 回跳到 redirectUri。
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| type | Integer | 是 | 社交类型,企业微信固定传 30 |
| code | String | 是 | 企业微信回调带回的授权码 |
| state | String | 是 | 企业微信回调带回的 state |
响应(首次登录与再次登录结构一致):
{
"code": 0,
"data": {
"userId": 123,
"accessToken": "xxx",
"refreshToken": "xxx",
"expiresTime": "2026-09-29T12:00:00"
}
}
social-auth-redirect(type=30)→ 拿到跳转地址 → 浏览器跳转到企业微信扫码页redirectUri,URL 携带 code 和 state → 由 social-login 页面承接type、code、state → 调用 social-loginaccessToken → 写入访问令牌存储 → 拉取权限信息 → 跳转首页关键点:第 4 步无论首次还是再次登录,返回结构完全一致,前端不需要区分两条分支。
| 字段 | 展示位置 | 说明 |
|---|---|---|
| accessToken | 登录成功后写入访问令牌存储 | 后续请求头携带 |
| refreshToken | 登录成功后写入访问令牌存储 | 令牌过期时刷新 |
| userId | 登录成功后写入用户信息存储 | 用于个人中心等场景 |
| 场景 | 规则 |
|---|---|
| 企业微信首次扫码(未绑定) | 自动创建系统用户,用户名为 wx_ + 8 位随机十六进制,昵称取企业微信昵称,账号状态为启用,随机生成密码(用户无需知晓,仅用于占位) |
| 首次注册后的默认权限 | 绑定角色 wxcp_user,**仅授予「会员管理」整棵子树**(根菜单 1075490 + 子菜单 + 孙菜单,共 15 条)。首页是前端静态路由、不依赖菜单表,任何账号都可见,无需授权。角色缺失时代码只记 warn 日志、不阻断登录,表现为「能进系统但菜单全空」 |
| 企业微信再次扫码(已绑定) | 直接登录,不创建新用户 |
| 非企业微信的社交平台首次登录 | 仍返回 1_002_000_005「未绑定账号,需要进行绑定」,需引导用户走账号密码登录并绑定 |
| 账号密码登录时携带社交参数 | 走原有绑定逻辑,登录成功后该社交账号与账号绑定 |
| 登录成功通知 | 通过企业微信发送一条文本卡片消息给该用户,内容含登录时间与登录方式;用户未绑定企业微信时跳过发送,不影响登录 |
| 通知发送失败 | 仅记录日志,**不阻塞、不回滚登录**,用户仍可正常进入系统 |
localhost 调试需用内网穿透并保证域名备案。system.user.register-enabled),但**仍受租户账号配额限制**,配额用尽时返回 USER_COUNT_MAX。wx_ 开头,便于与人工创建的账号区分;如需限制企业微信登录的用户范围,需在业务侧按用户名前缀或角色做二次管控。wxcp_user 必须先在库里存在。该角色及其「会员管理」授权已写进 docs/sql/wxcp_menu.sql 末尾(合并脚本 config_export_all_<日期>.sql 阶段三末尾同步了一份),新库初始化会自动建出来;**已建过的老库要补执行这一段**,否则扫码新账号能登录但菜单空白。代码按**角色编码**查找而非角色编号,换库不会失效。/admin-api/wxcp/callback 已加入免登录白名单,**不需要也不应该带前端令牌**。企业微信登录要跑通,以下三处配置**必须同时存在**:
| # | 配置位置 | 内容 | 缺失后果 |
|---|---|---|---|
| 1 | application-<profile>.yaml → wx.cp.* |
corp-id、agent-id、secret、token、aes-key | 回调接口无法解密,URL 验证失败 |
| 2 | application.yaml → justauth.type.WECHAT_ENTERPRISE(**全局,不随 profile 变**) |
client-id、client-secret、agent-id、**ignore-check-redirect-uri: true** |
调用 social-auth-redirect 抛 AuthException: Illegal redirect uri [WECHAT_ENTERPRISE] |
| 3 | 数据库 system_social_client |
social_type=30、**user_type=2**、client_id=CorpId、client_secret=Secret、agent_id、status=0、deleted=0 | 只用 yaml 里的密钥,无法在后台改;填错 user_type 则该行查不到 |
第 2 项容易漏掉:AuthRequestFactory 需要先从 yaml 拿到"社交平台基类配置"才能构造出对应的 AuthRequest 实例,之后才会用数据库里的密钥覆盖它。所以即使密钥全都配在数据库,yaml 里也必须保留一份占位配置。
第 2 项里 ignore-check-redirect-uri: true 最容易漏。yudao 的设计是 redirect-uri 不写死——SocialClientServiceImpl.getAuthorizeUrl 最后会用 HttpUtils.replaceUrlQuery 把 redirect_uri 换成前端按当前浏览器地址传进来的值。但 JustAuth 的 AuthDefaultRequest 构造时会调 AuthChecker.checkConfig(AuthConfig, AuthSource):
if (config.isIgnoreCheckRedirectUri()) {
return; // 有这个开关才放行
}
if (StringUtils.isEmpty(redirectUri)) {
throw new AuthException(AuthResponseStatus.ILLEGAL_REDIRECT_URI, source);
}
也就是说**不写 redirect-uri 的前提就是打开这个开关**,两者缺一不可。只删 redirect-uri 而漏了开关,扫码时会在 AuthRequestFactory.getDefaultRequest 构造 AuthWeChatEnterpriseQrcodeRequest 的那一刻抛 AuthException: Illegal redirect uri [WECHAT_ENTERPRISE]——异常发生在构造期,早于用数据库密钥覆盖的那一步,所以配置了 system_social_client 也救不了。
第 2 项为什么放 application.yaml 而不是某个 profile:YudaoJustAuthConfiguration 上的 @ConditionalOnProperty(prefix = "justauth", value = {"enabled"}, havingValue = "true", matchIfMissing = true) 让 AuthRequestFactory 无论配没配都会创建。缺配置不等于"关闭社交登录",而是一个运行期才暴露的坑:请求走到 AuthRequestFactory.get() 时先按 justauth.type 查不到,再回落到 justauth.extend 分支,而 JustAuthProperties.extend 默认就是 null,直接 NullPointerException。
这段配置历史上只存在于 application-local.yaml,导致用 dev / test / chdl / kyzn / wtxc / yzfx / zsjc 任一 profile 启动时,企业微信扫码登录必炸,并且报的是 AuthRequestFactory.get 第 104 行的 NPE,很难一眼看出是配置缺失。上移到全局配置文件后,这一类问题从结构上被消除。
代码层兜底:AuthRequestFactory.get() 已加判空守卫——properties.getExtend() 为 null 时跳过自定义分支,由既有逻辑抛出可读的 AuthException,不再抛 NullPointerException。
在企业微信管理后台 → 应用管理 → 自建应用 → 「接收消息」→「设置API接收」:
https://<已备案域名>/admin-api/wxcp/callbackwx.cp.token 完全一致wx.cp.aes-key 完全一致点「保存」时企业微信会立即发起一次 GET 验证,返回 echostr 明文即为通过。