统一认证服务(UAS)概要设计
###### 浓缩版 | 基于 v2 (2026-05-11) | 日期:2026-05-26
1. 项目背景与目标
1.1 背景
本项目作为"外部通行证"系统下的一个子服务,负责提供基于 Oauth2 思想的认证服务,提供基于 Token 的认证能力,是原来系统中基于 CAS 架构的认证服务(sso 服务)的补充与完善。
CAS 从 5.x 版本开始原生支持 OIDC Provider 功能,现有 SSO 服务的 CAS 版本为 4.2.6,升级成本较高;同时 CAS 必须依赖于 Cookies/浏览器环境,不适用于小程序、APP 场景。因此选择在不影响原有登录认证流程的基础上,实现新的认证服务。
1.2 目标
- 标准化接入:后续所有业务系统(如 SCRM、网报、拼团、转介绍)如需实现无状态的身份认证/单点登录接入通行证体系,均可对接 UAS 进行身份认证。
- 功能互补:UAS 除了提供基于 Token 的认证服务外,还提供了与原基于 Cookies/Session 的 CAS 单点登录服务桥接的认证方式,为原有的业务对接新服务提供了更加灵活的身份认证方案。
- 全面审计:UAS 会记录所有的身份认证请求,包括但不限于颁发授权码、颁发访问令牌、联合认证等操作,以便后续的审计与数据分析。
1.3 核心定位与业务场景
基于 OAuth2 思想,参照现有 CAS 的业务流程,只负责认证、授权、联合认证验证,不做其余功能与职责。
认证成功后用户端(H5、小程序)需保存 uas 的 access_token。
功能场景列举:
- 用户访问小程序 A 正常走微信授权码登录流程。
- 用户从小程序 A 跳转访问小程序 B,正常走授权登录流程。
- 用户从小程序 A 内部 webview 访问 H5 应用前,通过联合认证流程获取 uas_code 授权码并携带拼接到指定 SSO 登录链接完成登录(兼容原 CAS 登录逻辑)
2. 与现有系统的关系
┌──────────────────────────────────────────────────────────────────┐
│ 外部通行证系统 │
│ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ SSO │ │ UAS (本项目) │ │
│ │ Cookies/Session │◄── 联合认证 ──►│ OIDC/OAuth2.0 │ │
│ │ 传统 Web 认证 │ (身份互通) │ Token-Based │ │
│ └────────┬─────────┘ └──────────┬───────────┘ │
│ │ │ │
│ │ ┌──────────────┐ │ │
│ │ │ UCOUT │ │ │
│ └───────────►│ 用户中心 │◄────────┘ │
│ │ 对外服务 │ │
│ │ (用户资源) │ │
│ └──────┬───────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ │ │ │ │
│ ┌──────┴──────┐ ┌──────┴──────┐ ┌───────┴──────┐ │
│ │ MySQL │ │ Redis │ │ 微信开放平台 │ │
│ │ (共用) │ │ (共用) │ │ API │ │
│ └─────────────┘ └─────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────────┘
系统定位:
| 系统 | 定位 | 职责 |
|---|---|---|
| UCOUT | 用户资源服务 | 用户中心对外服务,提供账号注册/绑定/认证/微信解密等接口,是 SSO 和 UAS 共同依赖的用户数据与业务能力层 |
| SSO | 传统 Web 认证服务 | 基于 Cookies/Session 的 CAS 单点登录,服务于传统 Web 应用场景 |
| UAS (本项目) | 无状态认证服务 | 基于 OIDC/OAuth2.0 的 Token 认证服务,服务于小程序、APP 等无状态场景 |
| sso-extend | SSO 客户端扩展库 | 提供单点登出能力,UAS 后续可提供类似的客户端 SDK,或复用 sso-extend 的登出机制 |
SSO 与 UAS 的关系:
- 并列关系:SSO 和 UAS 是平行的认证服务,分别服务于不同的应用场景(传统 Web vs 小程序/APP)。
- 身份互通:通过认证桥接,UAS 可将 Token 身份转换为 SSO 的 Cookies/Session 身份,实现小程序到 H5 应用的静默登录。
- UAS 可包装 SSO:UAS 后续可能再包装一层,通过 SSO 完成登录流程(如 UAS 作为 OIDC Provider,内部委托 SSO 完成用户认证),从而复用 SSO 已有的认证页面和流程。
- 共同依赖 UCOUT:SSO 和 UAS 均直接请求 UCOUT 获取用户数据与业务能力,UCOUT 作为两者的用户资源服务层。
UAS 对 UCOUT 的核心依赖接口:
| UCOUT 接口 | 用途 | 调用方 |
|---|---|---|
/auth | 通行证账号认证(用户名+密码等) | UAS 服务端 |
/getWechatTypeByAppId | 根据微信 AppId 获取 wechat\_type | UAS 服务端 |
/retrieveOpenInfo | 根据微信 code + wechat\_type 获取 openid/unionid | UAS 服务端 |
/wechat/getPhone | 微信手机号解密,完成通行证注册与绑定 | UAS 服务端 |
***
3. 核心业务流程
3.1 适用于微信小程序的登录认证功能
3.1.1 设计目标
微信小程序无法使用原SSO登录方式,也无法使用标准授权码模式(无浏览器重定向能力),因此 UAS 提供基于微信授权码获取身份令牌的功能。
3.1.2 核心流程:
详细步骤:
- 小程序调用
wx.login()获取wx_login_code,并通过 UCOUT 接口/getWechatTypeByAppId查询wechat_type。 - 小程序携带
wx_login_code+wechat_type请求 UAS 授权码端点POST /oauth2/code/miniprogram。 - UAS 收到请求后,调用 UCOUT
/retrieveOpenInfo接口,根据wx_login_code+wechat_type获取解密后的openid、unionid(可能没有)。 - UAS 查询通行证绑定状态:
- 已绑定通行证:生成
uas_login_code并返回给小程序。 - 未绑定通行证:返回错误码
WECHAT_NOT_BOUND,要求小程序显示手机号授权按钮。
- 小程序携带
uas_login_code请求业务后端登录接口。 - 业务后端向 UAS 标准令牌端点
POST /oauth2/token发起请求,使用标准授权码模式换取令牌:
grant_type=authorization_codecode=uas_login_codeclient_id=xxxclient_secret=xxx
- UAS 验证
uas_login_code有效后,颁发access_token+uas_refresh_token+id_token。 - 业务后端收到令牌后,自行处理业务登录令牌(如直接使用
access_token或基于pptid+uas_refresh_token生成自己的业务登录令牌)。
未绑定通行证时的补充流程:
未绑定通行证详细步骤:
- 小程序请求 UAS 授权码端点,UAS 发现微信账号未绑定通行证,返回
wechat_not_bound错误。 - 小程序显示手机号授权按钮,用户点击授权后获取
wx_phone_code。 - 小程序再次请求 UAS 授权码端点,额外携带
wx_phone_code。 - UAS 调用 UCOUT
/wechat/getPhone完成通行证注册与绑定,获取pptid。 - UAS 生成
uas_login_code并返回。 - 后续步骤与已绑定流程一致。
3.2 SSO认证流程
3.2.1 设计目标
3.2.2 安全约束
uas_code一次性使用,验证后立即失效。uas_code绑定system_id,仅限目标系统验证使用。uas_code短时效,防止暴力破解。- 联合认证验证端点需验证请求来源(
client_id+client_secret),防止未授权系统调用。
***
4. 安全约束
| 安全措施 | 说明 |
|---|---|
| HTTPS 传输 | 所有认证接口强制 HTTPS,确保敏感数据传输安全 |
| 授权码模式 | 采用 OAuth2.0 授权码模式(Authorization Code Grant),授权码一次性使用,短时效 |
| 非对称密钥签名 | JWT 采用非对称密钥签名,私钥由 UAS 持有,公钥通过 JWKS 端点分发,业务后端可独立验证令牌合法性 |
| 令牌有效期 | access\_token 合理有效期,refresh\_token 较长有效期(可配置) |
| 客户端密钥 | 机密客户端使用 client_secret 认证,密钥以哈希存储 |
| scope 限制 | 令牌的 scope 严格限制为客户端被授权的 scope,防止越权访问 |
***
5. 关键数据结构概要
5.1 MySQL
UAS 与 UCOUT 共用 MySQL 实例,UAS 仅用于审计数据记录,不创建与 UCOUT 重复的用户/认证相关表。
5.2 Redis
- uas_code(小程序授权码):UAS 颁发的中间态授权码,后续由业务后端用于换取令牌。
- uas_access_token(访问令牌缓存):访问令牌的快速查询缓存,用于令牌内省和验证。
- uas_rate_limit(限流计数):基于 Redis 令牌桶算法的限流计数器。
5.3 会话中 code/token/pptid 的关系
5.3.1 uas_code
由/uas/oauth2/authorize端点生成,默认5分钟有效,临时存储于 Reids,一次性使用,使用后删除,不落库
5.3.2 access_token
由/uas/oauth2/token端点生成,默认从不过期(暂定),由业务后端(可选)、小程序/前端(保存).
UAS 存储于 Redis,token version 异步落库(pp_acct.token_version),token 中包含 version 信息
暂定用于:①获取联合认证的 code。
5.3.3 uas_refresh_token(未来按需实现)
***
6. 接口端点设计
6.1 标准 OIDC 端点(Spring Authorization Server 内置)
| 端点 | 方法 | 说明 | 是否启用 |
|---|---|---|---|
/uas/.well-known/openid-configuration | GET | OIDC 发现端点,返回服务元数据 | 🤔 |
/uas/oauth2/authorize | GET/POST | 授权端点,标准授权码模式入口 | ✅ |
/uas/oauth2/token | POST | 令牌端点,支持多种 grant\_type | ✅ |
/uas/oauth2/jwks | GET | JWKS 端点,分发 RSA 公钥 | ✅ |
/uas/oauth2/userinfo | GET/POST | UserInfo 端点,返回用户信息 | ✅ |
/uas/oauth2/revoke | POST | 令牌撤销端点 | 🤔 |
/uas/oauth2/introspect | POST | 令牌内省端点 | 🤔 |
/uas/connect/logout | GET/POST | OIDC 登出端点 | 🤔 |
6.2 自定义端点
6.2.1 获取授权码
典型场景:微信环境小程序内部访问 H5(使用 sso 登录)
携带已有的 uas\_access\_token 请求 UAS 获取一次性的 uas_sso_code。
# 调用方:业务微信小程序
POST https://pre-yy.zy.com/uas/auth/code
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {access_token}
authorize_type=sso_auth|mp_auth
# 成功响应
{
"uas_sso_code": "xxxx",
"expires_in": 300
}
6.2.2 获取令牌(微信授权码模式)
微信小程序使用此授权模式,用微信 code/phone_code 换取令牌。
# 调用方:业务微信小程序
POST https://pre-yy.zy.com/uas/auth/token
Content-Type: application/x-www-form-urlencoded
grant_type=mp_code|uas_code
&app_id=wxabdckfjdksa
&login_code=login_code
&phone_code=phone_code
&uas_code=uas_code[32位]
# 成功响应
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"uas_refresh_token": "OpWGgJhVZ...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "openid profile"
}
6.2.3 验证SSO登录授权码
UCOUT携带
# 调用方:业务微信小程序
POST https://pre-yy.zy.com/uas/auth/sso-verify
Content-Type: application/x-www-form-urlencoded
uas_sso_code=uas_sso_code
&client_id=xxx
&sign=sign
&ts=ts
# 成功响应
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"uas_refresh_token": "OpWGgJhVZ...",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 7200,
"scope": "openid profile"
}
***
7. 业务术语
| 术语 | 说明 |
|---|---|
| UAS | Unified Authentication Service,统一认证服务 |
| pptid | 通行证账号 ID,用户在"外部通行证"系统中的唯一标识 |
| uas\_login\_code | UAS 授权码,由 UAS 生成,用于微信小程序场景中交换访问令牌 |
| uas\_authorize\_code | UAS 联合认证码,由 UAS 生成,用于联合认证场景中进行身份串联 |
| uas\_access\_token | UAS 访问令牌,JWT 格式,包含用户身份信息,用于访问受保护资源 |
| uas\_refresh\_token | UAS 刷新令牌,用于在 access\_token 过期后获取新的 access\_token |
| id\_token | OIDC 身份令牌,JWT 格式,包含用户认证信息,供客户端验证用户身份 |
| wechat\_type | 微信应用类型标识,区分不同微信小程序/公众号 |
| system\_id | 业务系统标识,与 UCOUT 中的 system\_id 对应 |
| JWKS | JSON Web Key Set,JSON Web 密钥集合,用于分发公钥 |
***
8. 展望与后续演进
- PKCE 支持:为公共客户端(SPA、小程序)启用 PKCE(Proof Key for Code Exchange,S256),增加授权码的安全性,防止授权码被截获后被用于获取令牌。
- SPA 网页端场景:支持 SPA 网页端的标准 OAuth2.0 授权码模式 + PKCE,SPA 无法安全存储 client\_secret,需通过 PKCE 增强安全性。
- 动态配置热加载:将鉴权、降级限流等参数移入 Nacos 配置中心,支持运行时动态调整。
- 设备授权模式:支持 OAuth2.0 Device Authorization Grant,适配 IoT 设备、智能终端等无浏览器场景。
- SAML 2.0 桥接:通过 UAS 作为 SAML 2.0 Service Provider,实现与现有 SAML 2.0 集成(SSO 已支持 SAML 2.0)。
- 自适应认证:根据用户设备、IP、行为等风险因子,动态调整认证强度(如低风险静默登录,高风险要求二次验证)。
- 令牌加密:支持 JWT 加密(JWE),保护令牌中的敏感声明。
- 管理后台:提供客户端注册管理、审计日志查询、令牌管理等功能的管理后台。
- CAS SSO 退役:当所有业务系统迁移到 UAS 后,逐步退役 CAS SSO 服务。