登录体系统一整合方案

日期:2026-06-29 | 基于《统一认证服务(UAS)概要设计 v3》及 VTS 消息推送需求


1. 现状梳理与问题定义

1.1 涉及系统与组件总览

系统/组件认证方式身份标识openID/unionid 存储位置
微信小程序(多个)wx.login -> UAS (grant_type=mp_code) -> access_tokenpptid(通行证ID)UCOUT(通过 retrieveOpenInfo 获取,绑定关系存通行证)
微信公众号 — 卓越会员微服务OAuth2 网页授权openID微信代理(wxprx)记录 openID
城市服务号(多个,按城市申请)OAuth2 网页授权openID(各服务号独立)微信代理 / 教学服务单独保存 openID 与 memberID 关系
H5/Web 业务系统(网报等)SSO(CAS 4.2.6,Cookies/Session)system_id + 业务用户ID不直接存储 openID
UAS 统一认证服务OAuth2/OIDC Token-Basedpptid + access_token通过 UCOUT 查询微信绑定
UCOUT 用户中心内部接口服务pptid存储通行证与微信绑定关系
会员库内部数据服务家长 memberid / 学员 memberid不存储 openID
VTS 消息推送服务内部服务调用城市+服务号 appid + openIDVTS 自身维护城市+服务号关系,openID 依赖外部
微信代理(wxprx)微信 API 代理openID记录 openID 解密结果
TMS/evip业务系统,各自认证学员编号(与会员库 memberid 1:1)不存储 openID

1.2 各端登录流程现状

graph TB subgraph 微信小程序登录 A1[wx.login] --> A2[获取微信 code] A2 --> A3[UCOUT retrieveOpenInfo] A3 --> A4[获取 openid/unionid] A4 --> A5{已绑定通行证?} A5 -->|是| A6[UAS 生成 access_token] A5 -->|否| A7[手机号授权绑定] A7 --> A6 end subgraph 公众号H5登录 B1[用户访问H5页面] --> B2[重定向微信 OAuth2] B2 --> B3[获取微信 code] B3 --> B4[换取 openid] B4 --> B5[业务系统自行处理] end subgraph Web系统SSO登录 C1[访问业务系统] --> C2[重定向 SSO 登录页] C2 --> C3[用户名+密码认证] C3 --> C4[CAS Ticket 验证] C4 --> C5[建立 Session] end subgraph 小程序到H5联合认证 D1[小程序已有 access_token] --> D2[请求 UAS 获取 uas_sso_code] D2 --> D3[携带 uas_sso_code 跳转 H5] D3 --> D4[H5 用 uas_sso_code 完成 SSO 登录] end

1.3 核心问题分析

问题一:openID/unionid 分散

现状

断裂点

  1. 城市服务号与通行证之间没有直接关联,openID 存于教学服务
  2. 全日制业务不使用通行证,其用户的 openID 与通行证体系完全脱节
  3. 不同公众号的 openID 无法直接互通,依赖 unionid 桥接但覆盖不全

问题二:多端登录不互通

现状

断裂点

  1. 公众号 H5 场景下获取的 openID 无法与 UAS 通行证身份关联
  2. SSO 登录后无法获取 UAS Token,Web 端与小程序端身份不互通
  3. 各业务系统(网报、TMS/evip)接入认证方式不统一

问题三:消息推送与身份脱节

现状

断裂点

  1. 用户更换城市后,新城市服务号的 openID 可能未被记录
  2. 会员库 memberid 与服务号 openID 之间缺少统一映射
  3. "不关注公众号也可获取 openID"的场景下,openID 的存储与查询链路不清晰

2. openID/unionid 统一管理方案

2.1 核心原则

  1. unionid 作为微信生态层的统一用户标识:所有公众号、小程序必须绑定到同一微信开放平台账号,确保 unionid 可获取
  2. openID 映射集中管理:建立统一的 openID 映射表,收口各渠道的 openID 数据
  3. 兼容非通行证用户:全日制等不使用通行证的业务,通过会员 memberid 关联,不强制绑定通行证

2.2 微信开放平台绑定要求

应用类型绑定要求当前状态(需确认)
微信小程序 A绑定开放平台,可获取 unionid待确认
微信小程序 B绑定开放平台,可获取 unionid待确认
卓越会员微服务(公众号)绑定开放平台,可获取 unionid待确认
城市服务号 1绑定开放平台,可获取 unionid待确认
城市服务号 2绑定开放平台,可获取 unionid待确认
城市服务号 N绑定开放平台,可获取 unionid待确认

前提条件:所有应用必须绑定到同一微信开放平台账号,否则无法获取 unionid,跨应用身份打通将无法实现。

2.3 openID 映射表设计

在 UCOUT 服务中新建统一的微信身份映射表,收口所有 openID 数据:

-- 微信身份映射表(集中管理所有公众号/小程序的 openID)
CREATE TABLE pp_wechat_openid_map (
    id              BIGINT AUTO_INCREMENT PRIMARY KEY,
    unionid         VARCHAR(64)  NULL     COMMENT '微信 unionid(开放平台绑定后可获取)',
    openid          VARCHAR(64)  NOT NULL COMMENT '微信 openid',
    wechat_type     VARCHAR(32)  NOT NULL COMMENT '微信应用类型标识(对应 UCOUT wechat_type)',
    app_id          VARCHAR(64)  NOT NULL COMMENT '微信应用 AppID',
    app_category    VARCHAR(16)  NOT NULL COMMENT '应用类别:miniprogram / mp_service / mp_subscribe',
    pptid           BIGINT       NULL     COMMENT '通行证 ID(已绑定时关联)',
    member_id       BIGINT       NULL     COMMENT '会员库 memberid(非通行证用户关联)',
    bindsource      VARCHAR(16)  NULL     COMMENT '绑定来源:passport / member / teaching',
    status          TINYINT      DEFAULT 1 COMMENT '状态:1-有效 0-无效',
    created_at      DATETIME     DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    UNIQUE KEY uk_openid_apptype (openid, wechat_type),
    KEY idx_unionid (unionid),
    KEY idx_pptid (pptid),
    KEY idx_member_id (member_id),
    KEY idx_appid (app_id)
) COMMENT '微信 openID 统一映射表';

表设计说明

2.4 与 UCOUT 现有接口的关系

当前 UCOUT 提供的微信相关接口:

接口用途改造方案
/getWechatTypeByAppId根据 AppId 获取 wechat_type无需改造
/retrieveOpenInfo根据 code + wechat_type 获取 openid/unionid改造:获取到 openid/unionid 后,自动写入 pp_wechat_openid_map
/auth通行证账号认证无需改造
/wechat/getPhone微信手机号解密,完成注册与绑定改造:绑定成功后同步更新 pp_wechat_openid_map 的 pptid

新增接口

# 根据 unionid 查询用户在指定公众号下的 openID
POST /ucout/wechat/openid/query
Content-Type: application/json

{
    "unionid": "xxx",          // 或 member_id
    "target_app_id": "xxx"     // 目标公众号/小程序的 AppID
}

# 响应
{
    "openid": "xxx",
    "wechat_type": "xxx",
    "bindstatus": "bound"      // bound / unbound
}
# 批量同步 openID 数据(用于历史数据迁移)
POST /ucout/wechat/openid/sync
Content-Type: application/json

{
    "records": [
        {
            "openid": "xxx",
            "wechat_type": "xxx",
            "unionid": "xxx",
            "pptid": 123,
            "member_id": 456,
            "bindsource": "teaching"
        }
    ]
}

2.5 非通行证用户的兼容方案

针对全日制等不使用通行证的业务:

  1. 通过会员 memberid 关联:openID 映射表中 pptid 为空时,使用 member_id 作为关联键
  2. 双通道查询:VTS 查询 openID 时,优先通过 unionid 查询,其次通过 member_id 查询
  3. 渐进式绑定:非通行证用户后续如使用小程序等功能,引导绑定通行证后自动补充 pptid

3. 多端登录互通方案

3.1 目标

所有端(小程序、公众号 H5、Web)的登录认证统一收口到 UAS,各业务系统只需对接 UAS 即可完成身份认证。

3.2 各端登录流程统一设计

3.2.1 小程序登录(保持不变)

sequenceDiagram participant 小程序 participant 微信 participant UAS participant UCOUT participant 业务后端 小程序->>微信: wx.login() 微信-->>小程序: wx_code 小程序->>UAS: POST /oauth2/token { grant_type=mp_code, wx_code, app_id } UAS->>UCOUT: retrieveOpenInfo(wx_code, wechat_type) UCOUT-->>UAS: openid, unionid UAS->>UAS: 写入 pp_wechat_openid_map UAS->>UCOUT: 查询通行证绑定 alt 已绑定 UAS->>UAS: 生成 access_token else 未绑定 小程序->>微信: 手机号授权 微信-->>小程序: phone_code 小程序->>UAS: POST /oauth2/token { grant_type=mp_code, phone_code } UAS->>UCOUT: getPhone 注册绑定 UCOUT-->>UAS: pptid UAS->>UAS: 更新 pp_wechat_openid_map.pptid end UAS-->>小程序: access_token 小程序->>业务后端: 携带 access_token 业务后端->>UAS: POST /oauth2/userinfo { access_token } UAS-->>业务后端: user_info (pptid, openid, unionid)

3.2.2 公众号 H5 登录(新增 grant_type)

场景:用户在公众号内访问 H5 页面,通过公众号 OAuth2 授权获取身份。

新增 UAS grant_type:mp_oauth(公众号网页授权模式)

sequenceDiagram participant 用户 participant 公众号H5 participant 微信 participant UAS participant UCOUT participant 业务后端 用户->>公众号H5: 访问 H5 页面 公众号H5->>微信: 重定向 OAuth2 授权(scope=snsapi_base) 微信-->>公众号H5: 微信 code 公众号H5->>UAS: POST /oauth2/token { grant_type=mp_oauth, wx_code, app_id } UAS->>UCOUT: retrieveOpenInfo(wx_code, wechat_type) UCOUT-->>UAS: openid, unionid UAS->>UAS: 写入 pp_wechat_openid_map UAS->>UCOUT: 根据 unionid 查询通行证绑定 alt 已绑定通行证 UAS->>UAS: 生成 access_token UAS-->>公众号H5: { access_token, pptid, openid } else 未绑定通行证 UAS-->>公众号H5: { error: wechat_not_bound, openid, unionid } Note over 公众号H5: 引导手机号授权或静默使用 openid 身份 end 公众号H5->>业务后端: 携带 access_token 业务后端->>UAS: POST /oauth2/userinfo UAS-->>业务后端: user_info

接口定义

# 公众号 H5 网页授权模式获取令牌
POST /uas/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=mp_oauth
&app_id=wxXXX
&login_code=微信OAuth2授权码
&client_id=xxx
&client_secret=xxx

# 成功响应
{
    "access_token": "eyJhbGciOiJSUzI1NiIs...",
    "token_type": "Bearer",
    "expires_in": 7200,
    "scope": "openid profile",
    "pptid": 12345,
    "openid": "xxx",
    "unionid": "xxx"
}

3.2.3 小程序 -> H5 联合认证(保持现有方案)

现有 UAS 联合认证方案(authorize_type=sso_auth)已满足此场景,无需改造:

  1. 小程序使用已有 access_token 请求 UAS 获取 uas_sso_code
  2. 携带 uas_sso_code 跳转到 H5 页面的 SSO 登录链接
  3. H5 页面通过 SSO 验证 uas_sso_code 完成登录

3.2.4 Web 端 SSO 登录(保持 + 补充 Token 互认)

保持现有 SSO 登录流程不变,补充 SSO -> UAS Token 互认能力:

sequenceDiagram participant 浏览器 participant 业务系统 participant SSO participant UAS participant UCOUT 浏览器->>业务系统: 访问受保护资源 业务系统-->>浏览器: 重定向到 SSO 浏览器->>SSO: SSO 登录(用户名+密码 / 已有 Session) SSO-->>浏览器: CAS Ticket 浏览器->>业务系统: 携带 CAS Ticket 业务系统->>SSO: 验证 Ticket SSO-->>业务系统: 认证成功 + pptid Note over 业务系统: 可选:向 UAS 换取 access_token 业务系统->>UAS: POST /oauth2/token { grant_type=sso_bridge, sso_ticket, pptid } UAS-->>业务系统: access_token

新增 UAS grant_type:sso_bridge(SSO 桥接模式),让 Web 端业务系统在 SSO 登录后也能获取 UAS Token,实现与小程序端的身份互通。

# SSO 桥接模式获取 UAS Token
POST /uas/oauth2/token
Content-Type: application/x-www-form-urlencoded

grant_type=sso_bridge
&sso_ticket=ST-xxx
&client_id=xxx
&client_secret=xxx

3.2.5 各业务系统统一接入规范

接入步骤说明
1. 注册 UAS 客户端在 UAS 注册 client_id / client_secret,配置回调地址
2. 选择认证模式小程序用 mp_code,公众号 H5 用 mp_oauth,Web 用 sso_bridge 或保持 SSO
3. 实现登录对接调用 UAS /oauth2/token 获取 access_token
4. 获取用户信息调用 UAS /oauth2/userinfo 获取 pptid、openid 等
5. 业务令牌颁发基于 UAS 返回的 pptid 颁发业务系统自己的登录态

4. 消息推送与身份关联方案

4.1 问题定义

VTS 消息推送的核心链路:

业务系统触发事件 -> VTS 消息服务 -> 确定目标城市+服务号 -> 查找用户在该服务号下的 openID -> 发送模板消息

当前断裂点:VTS 无法可靠地查到"某用户在某城市服务号下的 openID"。

4.2 openID 查询方案

方案:VTS 通过 UCOUT 统一查询 openID

sequenceDiagram participant 业务系统 participant VTS participant UCOUT participant openID映射表 业务系统->>VTS: 发送消息请求(member_id, 城市, 消息模板) VTS->>VTS: 根据城市查找目标服务号 app_id VTS->>UCOUT: 查询 openID(member_id, app_id) UCOUT->>openID映射表: SELECT openid FROM pp_wechat_openid_map WHERE member_id=? AND app_id=? alt 通过 member_id 找到 openID映射表-->>UCOUT: openid else 通过 unionid 兜底 UCOUT->>openID映射表: SELECT openid WHERE unionid=(SELECT unionid WHERE member_id=?) AND app_id=? openID映射表-->>UCOUT: openid end UCOUT-->>VTS: openid VTS->>微信: 发送模板消息(openid + template_id) 微信-->>VTS: 发送结果 VTS-->>业务系统: 推送结果

4.3 openID 数据采集策略

针对不同场景,openID 写入映射表的时机和方式:

场景采集时机采集方式写入字段
小程序登录用户 wx.login 时UAS 调用 retrieveOpenInfo 后自动写入openid, unionid, wechat_type, app_id
公众号 H5 访问用户 OAuth2 授权时UAS mp_oauth 流程中自动写入openid, unionid, wechat_type, app_id
通行证绑定手机号授权绑定时UCOUT getPhone 回调中更新 pptidpptid, bindsource=passport
会员库关联会员注册/关联时会员库同步接口member_id, bindsource=member
城市服务号首次关注用户关注/进入公众号时微信事件推送 -> 写入映射表openid, app_id, app_category=mp_service
历史数据迁移一次性批量导入调用 /ucout/wechat/openid/sync 接口全字段

4.4 关键约束处理

约束 1:"不关注公众号也可获取 openID"

微信公众号 OAuth2 网页授权(scope=snsapi_base)可以在用户不关注公众号的情况下获取 openID。因此:

约束 2:"用户可在新城市公众号登录绑定"

用户跨城市场景:

  1. 用户原在城市 A 的服务号有 openID_A
  2. 用户到城市 B 后,进入城市 B 公众号,OAuth2 授权获取 openID_B
  3. 通过 unionid 关联,系统识别为同一用户
  4. openID_B 自动写入映射表,VTS 后续可向城市 B 服务号推送消息

约束 3:"openid 不在通行证关联"

对于全日制等不使用通行证的业务:

4.5 VTS 对接接口

UCOUT 新增查询接口

# VTS 查询目标 openID
POST /ucout/wechat/openid/resolve
Content-Type: application/json

{
    "member_id": 12345,        // 会员 memberid(优先)
    "unionid": "xxx",          // 或 unionid(兜底)
    "target_app_id": "wxXXX"   // 目标服务号 AppID
}

# 响应
{
    "openid": "oXXXX",         // 目标服务号下的 openID
    "found": true
}

VTS 服务号配置表(VTS 自身维护):

-- VTS 城市与服务号关系(语雀文档中已规划)
CREATE TABLE vts_city_service (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    city_code   VARCHAR(16)  NOT NULL COMMENT '城市编码',
    app_id      VARCHAR(64)  NOT NULL COMMENT '服务号 AppID',
    app_name    VARCHAR(128) NULL     COMMENT '服务号名称',
    status      TINYINT      DEFAULT 1,
    created_at  DATETIME     DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_city_app (city_code, app_id)
);

-- VTS 服务号与模板关系(语雀文档中已规划)
CREATE TABLE vts_template_config (
    id           BIGINT AUTO_INCREMENT PRIMARY KEY,
    app_id       VARCHAR(64)  NOT NULL COMMENT '服务号 AppID',
    template_id  VARCHAR(128) NOT NULL COMMENT '微信模板消息 ID',
    msg_type     VARCHAR(64)  NOT NULL COMMENT '消息类型标识',
    status       TINYINT      DEFAULT 1,
    created_at   DATETIME     DEFAULT CURRENT_TIMESTAMP
);

5. 整合架构总览

5.1 整合后全链路架构图

graph TB subgraph 微信生态层 MP1[小程序 A] MP2[小程序 B] GZH1[卓越会员微服务-公众号] GZH2[城市服务号 A] GZH3[城市服务号 B] OPEN[微信开放平台-unionid] end subgraph 认证服务层 UAS[UAS 统一认证服务
mp_code / mp_oauth / sso_bridge / authorization_code] SSO[SSO 认证服务
CAS Cookies/Session] end subgraph 用户数据层 UCOUT[UCOUT 用户中心] OIDMAP[openID 统一映射表
pp_wechat_openid_map] MEMBER[会员库
家长-学员关系] end subgraph 业务系统层 WB[网报系统] TMS[TMS/evip] OTHER[其他业务系统] end subgraph 消息推送层 VTS[VTS 消息服务] end subgraph 代理服务层 WXPRX[微信代理 wxprx] end MP1 -->|wx.login| UAS MP2 -->|wx.login| UAS GZH1 -->|OAuth2| UAS GZH2 -->|OAuth2| UAS GZH3 -->|OAuth2| UAS UAS -->|retrieveOpenInfo| UCOUT UAS -->|写入 openID| OIDMAP UCOUT -->|用户数据| MEMBER SSO <-->|联合认证 uas_sso_code| UAS WB -->|access_token| UAS TMS -->|access_token| UAS OTHER -->|access_token| UAS WB -->|CAS Ticket| SSO VTS -->|查询 openID| OIDMAP VTS -->|城市+服务号配置| VTS VTS -->|模板消息| GZH2 VTS -->|模板消息| GZH3 WXPRX -->|openID 解密| UCOUT MP1 -.->|绑定开放平台| OPEN MP2 -.->|绑定开放平台| OPEN GZH1 -.->|绑定开放平台| OPEN GZH2 -.->|绑定开放平台| OPEN GZH3 -.->|绑定开放平台| OPEN

5.2 核心数据流向

graph LR subgraph 身份建立 A[微信授权] --> B[UAS 认证] B --> C[openID 写入映射表] B --> D[通行证/会员关联] end subgraph 业务使用 D --> E[业务系统获取用户信息] E --> F[业务操作] end subgraph 消息推送 F --> G[触发消息事件] G --> H[VTS 查询 openID] H --> I[从映射表获取目标 openID] I --> J[发送模板消息] end

6. 补充说明

6.1 迁移策略

建议分三个阶段推进:

第一阶段:基础设施搭建(1-2 周)

步骤内容负责方
1确认所有公众号/小程序是否已绑定同一微信开放平台产品/运营
2在 UCOUT 中创建 pp_wechat_openid_map通行证开发
3实现 UCOUT openID 查询/同步接口通行证开发
4历史 openID 数据批量迁移(从微信代理、教学服务同步)通行证开发 + 相关系统

第二阶段:认证层改造(2-4 周)

步骤内容负责方
5UAS 新增 mp_oauth grant_type(公众号 H5 场景)UAS 开发
6UAS 新增 sso_bridge grant_type(SSO 桥接场景)UAS 开发
7改造 UAS Token 颁发流程,自动写入 openID 映射表UAS 开发
8公众号 H5 页面对接 UAS mp_oauth 登录前端开发

第三阶段:业务系统接入与消息推送(2-4 周)

步骤内容负责方
9网报系统接入 UAS 统一认证网报开发
10TMS/evip 接入 UAS 统一认证TMS/evip 开发
11VTS 对接 UCOUT openID 查询接口VTS 开发
12各业务系统消息推送对接 VTS各业务系统开发
13端到端联调与测试全员

6.2 风险评估

风险项风险等级影响应对措施
部分应用未绑定微信开放平台无法获取 unionid,跨应用身份打通失败优先排查并完成绑定,作为第一阶段第一优先级
历史 openID 数据迁移不完整VTS 找不到部分用户的 openID,消息推送失败分批迁移 + 用户下次访问时自动补充
unionid 覆盖不全(历史用户无 unionid)无法通过 unionid 关联多端身份通过手机号匹配 + 引导用户重新授权
全日制业务不使用通行证需要 member_id 双通道关联映射表已设计兼容方案,影响可控
各业务系统改造进度不一致部分系统仍使用旧认证方式UAS 兼容旧 SSO 流程,渐进式迁移
城市服务号新增/变更频繁VTS 配置需同步更新VTS 提供管理后台,支持动态配置

6.3 与 UAS 概要设计文档的关系

文档定位
统一认证服务(UAS)概要设计 v3UAS 服务本身的技术设计,聚焦认证协议、接口端点、令牌机制
本文档(登录体系统一整合方案)上层整合方案,聚焦各系统间的身份打通、数据流转、统一接入规范

本文档是 UAS 概要设计的上层补充: