uni-app 云函数公共模块 uni-id-common 深入解析:token 创建、校验与刷新的完整实现与版本演进
【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app
导读
uni-id-common 是 uni-app 项目 src/uni_modules/uni-id-common 目录下提供的云函数公共模块,它封装了 uniCloud 体系中用户身份认证最核心的三项能力:token 创建(createToken)、token 校验(checkToken)与 token 刷新(refreshToken)。该模块由旧版 uni-id 公共模块简化而来,从 1.0.0 到 1.0.16 共经历了 16 个版本的迭代,逐步补齐了 token 存储、角色权限、多应用配置、国际化等关键能力。本文以该模块的 changelog.md 为骨架,结合 index.js 源码与 config.json 配置样例,逐版本还原其设计动机与底层实现,帮助你彻底理解 token 认证机制的运作原理,并掌握在自有云函数中接入该模块的完整方法。
一、模块定位:从 uni-id 到 uni-id-common 的简化
根据 package.json 的描述,该模块是“包含 uni-id token 生成、校验、刷新功能的云函数公共模块”,版本号为 1.0.16。而 changelog 中 1.0.0(2022-06-21)的发布说明也明确写道:
提供 uni-id token 创建、校验、刷新接口,简化旧版 uni-id 公共模块
这意味着 uni-id-common 是从功能庞杂的旧版 uni-id 公共模块中抽离出的精简核心——它只保留与 token 生命周期直接相关的逻辑,将注册、登录、找回密码等业务能力留在其他模块(如 uni-id-pages、uni-id-co)中实现。
在源码目录结构中,该模块以标准云函数公共模块形态存在:
src/uni_modules/uni-id-common/ ├── changelog.md # 版本更新日志 ├── package.json # 插件描述(uni_modules 规范) └── uniCloud/ └── cloudfunctions/ └── common/ └── uni-id-common/ ├── index.js # 核心实现(已压缩) └── package.json # 云函数公共模块依赖声明从 公共模块 package.json 可以看到,它对外暴露的入口是index.js,并声明了唯一运行时依赖uni-config-center(以file:../../../../../uni-config-center/...的相对路径方式引用),这正是 changelog 1.0.1 中“补充对 uni-config-center 的依赖”的源码体现。
二、核心架构:一个实例、三类接口、一套错误码
通过阅读 index.js 可以还原模块的整体设计:
- 对外暴露一个
createInstance工厂方法,通过module.exports = { createInstance }导出; - 实例内部封装了类
m(可理解为 UniID 实现类),并借助Proxy对所有公开方法做统一包装,实现错误码到国际化文案(errMsg)的自动转换; - 公开方法通过
Object.freeze冻结挂载到原型上,包括checkToken、createToken、refreshToken三个异步方法; - 模块内部定义了一整套错误码常量,例如
TOKEN_EXPIRED(uni-id-token-expired)、CHECK_TOKEN_FAILED(uni-id-check-token-failed)、ACCOUNT_BANNED(uni-id-account-banned)等,其中uni-id-token-expired对应 HTTP 状态码 30203、uni-id-check-token-failed对应 30202,便于云函数 URL 化等场景下客户端识别。
从源码结构可以推断,createInstance({ context, clientInfo, config })接受三个可选参数:context用于提取客户端信息(appId、platform、locale、clientIP、deviceId),clientInfo可直接传入这些信息,config则用于覆盖默认配置。未传入 config 时,实例会自动通过 uni-config-center 读取 uni-id 的配置文件(详见下文第四节)。
三、三大核心接口的源码级原理
1. createToken:创建 token 并写入用户表
createToken({ uid, role, permission })要求至少传入uid,否则抛出PARAM_REQUIRED(参数缺失)错误。内部流程(对应_createToken)为:
- 补齐角色权限:若调用方未传入
role/permission,则通过getUserPermission()从uni-id-users集合的用户记录中读取role字段,再根据角色去uni-id-roles集合查询对应的permission列表并去重; - 支持自定义 token 内容:若配置目录下存在
custom-token.js拦截器文件,则调用该文件中导出的函数(参数为{ uid, role, permission }),用返回值覆盖默认的 token payload; - 签发 JWT:使用 HMAC-SHA256 算法(
alg: HS256、typ: JWT)签名,payload 包含uid、role、permission、iat(签发时间)与exp(过期时间),并以uniIdVersion: "1.0.16"标识版本;签名密钥取自配置中的tokenSecret,有效期取自tokenExpiresIn; - 维护用户 token 数组:将新 token 追加到
uni-id-users表中该用户的token字段数组内,同时过滤掉已过期或早于valid_token_date(登出/改密后失效时间)的旧 token; - 返回结果:返回
{ errCode: 0, token, tokenExpired },其中tokenExpired为具体过期时间戳(毫秒)。
maxTokenLength 的作用:源码中const { tokenSecret, tokenExpiresIn, maxTokenLength = 10 } = this.config表明,changelog 1.0.16 新增的maxTokenLength配置用于限制数据库用户记录中 token 数组的最大长度,默认值为 10。每次写入前会执行l.length > a && l.splice(0, l.length - a)丢弃最旧的 token,防止用户多次登录后 token 数组无限膨胀。
2. checkToken:校验 token 并支持到期前自动续签
checkToken(token, { autoRefresh = true })的校验链路分为四步:
- 签名与结构校验:将 token 按
.分为 header、payload、signature 三段,用配置的tokenSecret重新计算 HMAC-SHA256 签名并比对,同时校验alg必须为HS256、typ必须为JWT; - 过期判定:当
exp对应的毫秒时间戳早于当前时间时,抛出TokenExpiredError并转换为TOKEN_EXPIRED错误码; - 角色权限回补:这是 changelog 1.0.9 引入的能力——当 token payload 内未缓存
role/permission(例如旧版本签发的 token)时,自动查库获取角色权限; - 阈值内自动续签:若配置了
tokenExpiresThreshold且autoRefresh为 true,当剩余有效期小于该阈值时,会调用_createToken({ uid })签发新 token 并随校验结果一并返回,实现“临近过期自动续期”的无感刷新。源码中checkConfig()还会校验tokenExpiresThreshold必须小于tokenExpiresIn,且当阈值超过有效期一半时输出警告日志,提示配置可能过大。
3. refreshToken:显式刷新 token
refreshToken({ token })接受旧 token,先走与 checkToken 相同的校验逻辑,取出其中的uid并读取该用户当前最新的角色权限,再签发全新 token 返回。与 checkToken 的自动续签相比,refreshToken 适合客户端主动发起刷新的场景(例如执行敏感操作前强制刷新)。
四、配置体系:uni-config-center 与多应用、多平台配置
uni-id-common 本身不携带业务配置,所有配置通过 uni-config-center 统一管理。仓库中的 uni-id 配置样例 展示了完整参数结构:
| 配置项 | 默认值(顶层示例) | 说明 |
|---|---|---|
tokenSecret | tokenSecret-demo | token 签名密钥,必填,生产环境必须更换 |
tokenExpiresIn | 7200 | token 有效期(秒),默认 2 小时 |
tokenExpiresThreshold | 3600 | 剩余有效期小于该值时自动续签(秒) |
passwordErrorLimit | 6 | 密码错误次数上限 |
passwordErrorRetryTime | 3600 | 密码错误锁定时间(秒) |
bindTokenToDevice | false | 是否将 token 绑定到设备 |
maxTokenLength | 10 | token 数组最大长度(1.0.16 新增) |
app/web/mp-weixin/mp-alipay | — | 按端覆盖 token 有效期与 OAuth 配置 |
从 uni-config-center 的 readme 可知,其核心价值在于“配置文件统一管理,分离插件主体和配置信息”。uni-id-common 的源码实现细节如下:
- 多应用配置:
_getOriginConfig()支持配置为数组或对象;当配置为数组时,通过dcloudAppid匹配当前请求对应的应用,未匹配到则回退到isDefaultConfig标记的默认应用。changelog 1.0.5 修复的“使用多应用配置时报Cannot read property 'appId' of undefined”即与此逻辑相关; - 平台名回退:
_getPlatformConfig()会先做平台名归一化——将旧的app-plus映射为app、h5映射为web,再按 web →h5、app →app-plus的映射关系读取对应平台的覆盖配置,最终与顶层默认配置深度合并,并强制要求存在tokenSecret与tokenExpiresIn。changelog 1.0.12 所述“读取配置文件时回退平台 app-plus、h5”正是这一兼容逻辑——读取旧平台名配置仍然可用,但官方推荐使用新平台名app、web进行配置; - 配置合法性校验:当
config.json缺失或格式不合法时,_getOriginConfig()会抛出带具体原因的Invalid uni-id config file错误,这对应 changelog 1.0.7 修复的“config 文件不合法时未抛出具体错误”的问题。
五、角色权限与数据库存储:token 背后的数据模型
从源码中可以确认该模块直接操作两个云数据库集合:
- uni-id-users:用户表,token 创建时读取
role、token、status、valid_token_date、last_login_ip、last_login_date等字段,并将新 token 写入token字段数组; - uni-id-roles:角色表,通过
role_id查询角色对应的permission权限列表。
getUserRecord()会依据用户status字段做状态拦截:0或未设置为正常,1封禁(ACCOUNT_BANNED)、2审核中(ACCOUNT_AUDITING)、3审核失败(ACCOUNT_AUDIT_FAILED)、4已注销(ACCOUNT_CLOSED)。
getUserPermission()的判定逻辑值得注意:用户角色为空时直接返回空角色与空权限;当角色包含admin时直接返回该角色且不再查权限表——这正是 changelog 1.0.14 修复的“admin 用户包含其他角色时未包含在 token 中”的问题,修复后 admin 角色的全部角色名都会被写入 token,而权限列表不再重复查询。
另外,源码中isTokenInDb(uniIdVersion)通过版本号比对(>= 1.0.10)来判断 token 是否存储在数据库中,对应 changelog 1.0.10“将 token 存储在用户表的 token 字段内,与旧版本 uni-id 保持一致”的存储策略变更,以及 1.0.13 修复的“创建 token 时未传角色权限信息生成的 token 不正确”的问题(即_createToken中未传 role/permission 时自动补齐)。
六、国际化与错误处理
changelog 1.0.15 提到“修复部分语言国际化出错的 Bug”,与之对应的实现是:模块内置了zh-Hans与en两套错误文案映射(如uni-id-token-expired→ “登录状态失效,token已过期” / “The login status is invalid, token has expired”),并通过uniCloud.initI18n初始化国际化实例,语言取自客户端信息中的locale,回退语言为zh-Hans;若 uni-config-center 的 uni-id 目录下存在lang/index.js文件,则会与内置文案合并,允许开发者覆盖或补充多语言文案。
结合createInstance外层Proxy的统一包装可以推断,所有公开方法的错误返回都会经过_t()转换:errMsg使用当前语言环境渲染,{param}之类的占位符会被实际参数替换,最终统一为{ errCode, errMsg, ... }结构,同时删除内部字段errMsgValue。
七、在云函数中接入:以 uni-pay-x 为实际范例
仓库中的 uni-pay-x 模块是该公共模块的真实消费方,其 uni-pay-co/index.obj.js 中的接入方式可以直接复用:
const uniIdCommon = require('uni-id-common'); // 在云函数对象构造时创建实例 this.uniIdCommon = uniIdCommon.createInstance({ context: this.getUniIdToken ? { /* 上下文信息 */ } : undefined, clientInfo: this.getClientInfo(), });在 middleware/auth.js 中通过中间件统一校验身份:
const payload = await this.uniIdCommon.checkToken(token);checkToken返回的 payload 中包含uid、role、permission以及自动续签产生的新 token(如有),业务云函数即可据此判断当前用户身份与权限。
接入该模块的步骤可概括为:
- 将
uni-id-common公共模块放入云函数的common目录(uni_modules 方式会自动处理依赖); - 在目标云函数的
package.json中声明依赖,参考 uni-pay-co/package.json 中的"uni-id-common": "file:.../uni-id-common/uniCloud/cloudfunctions/common/uni-id-common"写法; - 确保存在 uni-config-center 公共模块及其下
uni-id/config.json配置文件,并配置tokenSecret、tokenExpiresIn等必填项; - 在业务云函数中
require('uni-id-common')并调用createInstance(...)创建实例,即可使用createToken/checkToken/refreshToken三个接口。
八、版本演进速览(1.0.0 → 1.0.16)
结合 changelog 与源码,可将 16 个版本的演进归纳为四个阶段:
- 1.0.0 ~ 1.0.2(2022-06)基础落地期:1.0.0 提供 token 创建、校验、刷新接口,简化旧版 uni-id;1.0.1 补充对 uni-config-center 的依赖;1.0.2 对齐旧版本 uni-id 默认配置;
- 1.0.4 ~ 1.0.8(2022-06 至 2022-07)兼容修复期:修复自定义 token 内容报错(1.0.4)、多应用配置 appId 报错(1.0.5)、移除插件内数据表 schema(1.0.6)、config 文件不合法未抛出具体错误(1.0.7)、clientDB 默认依赖下取不到 uni-id 配置(1.0.8);
- 1.0.9 ~ 1.0.13(2022-07)权限与存储完善期:checkToken 兼容 token 内未缓存角色权限(1.0.9)、token 存储进用户表与旧版对齐(1.0.10)、修复
read property 'reduce' of undefined错误(1.0.11,对应权限去重逻辑中t.data.reduce的健壮性问题)、平台名回退 app-plus/h5(1.0.12)、修复创建 token 时未传角色权限导致 token 不正确(1.0.13); - 1.0.14 ~ 1.0.16(2023-03 至 2023-04)能力增强期:修复 admin 用户包含其他角色时未写入 token(1.0.14)、修复部分语言国际化错误(1.0.15)、新增
maxTokenLength配置限制 token 数组最大长度(1.0.16)。
从这段演进可以看出,uni-id-common 的每一次发版都围绕“兼容旧版 uni-id 生态、保证 token 数据一致性、完善配置与多端适配”三个目标展开。当前仓库内版本为 1.0.16,其完整实现均可在 index.js 中验证,配置样例可参考 uni-id/config.json。
结语
uni-id-common 虽是一个体量不大的云函数公共模块,却承载了 uniCloud 应用身份认证的关键链路。理解其 token 的 JWT 结构、双集合(uni-id-users / uni-id-roles)的数据读写、阈值自动续签机制以及 uni-config-center 的多应用多平台配置解析,是排查登录态失效、权限错误等线上问题的必备知识。开发者可直接在本仓库中按上述路径查看源码与配置,或参考 uni-pay-x 的实际接入写法,将这套成熟的身份认证能力复用到自己的云函数项目中。
【免费下载链接】uni-appA cross-platform framework using Vue.js项目地址: https://gitcode.com/gh_mirrors/un/uni-app
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考