# 云对象(uni-id-co)

uni-id-co是uni-id-pages的核心云对象,包含了诸多用户相关的接口。作为uni-id体系的一部分,uni-id-co也使用uni-id的配置文件(cloudfunctions/common/uni-config-center/uni-id/config.json)。目前此云对象依赖了一些客户端信息,不支持被其他云函数/云对象调用。已支持url化调用,参考:uni-id-co url化

前端调用云对象uni-id-co内的方法前应先获取云对象的引用,代码如下

const uniIdCo = uniCloud.importObject('uni-id-co')

如调用uni-id-co方法时出现找不到lodash.merge模块的错误,请手动在uni-id-co云对象目录执行npm install。如果是运行客户端的话uniCloud插件会自动给uni-id-co安装缺失的依赖。

# 目录说明

├─common 					// 公用逻辑
├─config 					// 配置
│  └─permission.js 			// 调用接口所需的权限配置
├─lang 						// 国际化目录
├─lib 						// 基础功能,不建议修改此目录下文件
│  ├─third-party			// 三方平台接口
│  └─utils					// 基础功能
├─middleware				// 中间件
└─module					// 分模块存放的云对象方法

# 公共响应参数

uni-id-co所有api返回值均满足uniCloud响应体规范

返回值示例

{
	errCode: 0, // 错误码,详见错误码列表
			errMsg: '', // 错误信息,uni-id-co会自动根据客户端语言对错误信息进行国际化
			newToken: { // 注册、登录、刷新token等接口会自动返回新token,uniCloud客户端sdk会自动将新token及过期时间存储在storage内(需要HBuilderX 3.4.13及以上版本)
		token,
				tokenExpired
	},
	// ...其余参数
}

注意

  • 需要校验token的接口在token即将过期时也会返回newToken,token即将过期的阈值由开发者自行配置

# 适配URL化

uni-id-pages@1.0.29版本起支持URL化 什么是URL化

调用规范:

  1. HTTP 请求头中的Content-Type必须为application/json,请求方法必须是POST, 如不满足条件将会返回 uni-id-unsupported-request 错误码
  2. 请求体需按照以下格式:
{
	"clientInfo": {},
	"uniIdToken": "",
	"params": {}
}

字段说明

字段 说明
clientInfo 客户端信息; uni.getSystemInfo返回的字段
uniIdToken 用户Token; 用户登录后必填
params API接口参数字段

如果是在 uni-app 之外的应用中调用 URL 化接口,请确保clientInfo中存在以下字段:

字段 说明
uniPlatform 应用运行平台,与条件编译平台相同。详见
appId manifest 中应用appid,即DCloud appid。如没有请手动指定一个,需确保唯一性。
deviceId 设备 id;通过uni.getSystemInfo获取

假设已在uniCloud 控制台已设置URL化域名PATH,以PATH为/http/uni-id-co为例,演示登录示例:

uni.request({
	url: 'https://{云函数Url化域名}/http/uni-id-co/login',
	method: 'POST',
	data: {
		clientInfo: uni.getSystemInfoSync(),
		uniIdToken: '用户Token',
		params: {
			username:"username",
			password:"password"
		}
	},
	header: {
		'Content-Type': 'application/json'
	},
	success: (res) => {
		// 返回值
	}
})

注意

请不要添加 Query 参数,URL化后将忽略 Query 参数

# API列表

API 描述
uniIdCo.registerAdmin() 注册管理员 详情
uniIdCo.addUser() 新增用户 详情
uniIdCo.authorizeAppLogin() 授权用户登录应用 详情
uniIdCo.removeAuthorizedApp() 移除用户登录授权 详情
uniIdCo.setAuthorizedApp() 设置用户允许登录的应用列表 详情
uniIdCo.registerUser() 注册普通用户 详情
uniIdCo.registerUserByEmail() 通过邮箱验证码注册普通用户 详情
uniIdCo.login() 用户名密码登录 详情
uniIdCo.loginBySms() 短信验证码登录 详情
uniIdCo.loginByUniverify() App端一键登录 详情
uniIdCo.loginByWeixin() 微信登录 详情
uniIdCo.loginByAlipay() 支付宝登录 详情
uniIdCo.loginByQQ() QQ登录 详情
uniIdCo.loginByApple() 苹果登录 详情
uniIdCo.logout() 用户退出登录 详情
uniIdCo.bindMobileBySms() 通过短信验证码绑定手机号 详情
uniIdCo.bindMobileByUniverify() 通过一键登录绑定手机号 详情
uniIdCo.bindMobileByMpWeixin() 通过微信绑定手机号 详情
uniIdCo.bindWeixin() 绑定微信 详情
uniIdCo.bindQQ() 绑定QQ 详情
uniIdCo.bindAlipay() 绑定支付宝账号 详情
uniIdCo.bindApple() 绑定苹果账号 详情
uniIdCo.updatePwd() 更新密码 详情
uniIdCo.resetPwdBySms() 通过短信验证码重置密码 详情
uniIdCo.resetPwdByEmail() 通过邮箱验证码重置密码 详情
uniIdCo.closeAccount() 注销账户 详情
uniIdCo.getAccountInfo() 获取账户账户简略信息 详情
uniIdCo.createCaptcha() 创建图形验证码 详情
uniIdCo.refreshCaptcha() 刷新图形验证码 详情
uniIdCo.sendSmsCode() 发送短信验证码 详情
uniIdCo.sendEmailCode() 发送邮箱验证码 详情
uniIdCo.refreshToken() 刷新token 详情
uniIdCo.acceptInvite() 接受邀请 详情
uniIdCo.getInvitedUser() 获取受邀用户 详情
uniIdCo.setPushCid() 更新device表的push_clien_id 详情
uniIdCo.getSupportedLoginType() 获取支持的登录方式 详情

# 注册登录和登出

# 注册超级管理员

接口名:registerAdmin

接口形式

await uniIdCo.registerAdmin({
	username,
	password,
	nickname
})

参数说明

参数名 类型 必填 说明
username string 超级管理员用户名
password string 超级管理员密码
nickname string 超级管理员昵称

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 系统中仅可存在一个超级管理员

# 用户名密码注册用户

接口名:registerUser

接口形式

await uniIdCo.registerUser({
	username,
	password,
	captcha,
	nickname,
	inviteCode
})

参数说明

参数名 类型 必填 说明
username string 用户名
password string 密码
captcha string 图形验证码
nickname string 昵称
inviteCode string 邀请码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 邮箱验证码注册用户

接口名:registerUserByEmail

接口形式

await uniIdCo.registerUserByEmail({
	email,
	password,
	code,
	nickname,
	inviteCode
})

参数说明

参数名 类型 必填 说明
email string 邮箱
password string 密码
code string 邮箱验证码
nickname string 昵称
inviteCode string 邀请码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 密码登录

接口名:login

接口形式

await uniIdCo.login({
	username,
	password,
	captcha
})

参数说明

参数名 类型 必填 说明
username string 和mobile、email三选一 用户名
mobile string 和username、email三选一 手机号
email string 和username、mobile三选一 邮箱
password string 密码
captcha string 否(此操作错误3次以上为必填) 图形验证码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 短信验证码登录

手机号已存在时登录,否则注册

接口名:loginBySms

接口形式

await uniIdCo.loginBySms({
	mobile,
	code,
	captcha,
	inviteCode
})

参数说明

参数名 类型 必填 说明
mobile string 手机号
code string 短信验证码
captcha string 否(此操作错误3次以上为必填) 图形验证码
inviteCode string 邀请码,仅注册时生效

两小时内登录失败3次的用户必填图形验证码,如果客户端没有使用uni-id-pages,可以参考uni-id-pages验证码登录页面的相关逻辑。

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 微信授权手机号登录uni-id-co 1.0.25+

接口名:loginByWeixinMobile

接口形式

await uniIdCo.loginByWeixinMobile({
	phoneCode,
	inviteCode
})

参数说明

参数名 类型 必填 说明
phoneCode string getPhoneNumber 事件回调获取到动态令牌code
inviteCode string 邀请码,仅注册时生效

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 此接口会调用微信凭证接口获取access_token,uni-id-pages 1.0.8及以上版本会使用 uni-open-bridge-common 保存 access_token 信息。
  • 如果开发者在其他应用未使用 uni-open-bridge-common 管理 access_token 等信息,可能会造成 access_token 冲突。

# 一键登录

手机号已存在时登录,否则注册

接口名:loginByUniverify

接口形式

await uniIdCo.loginByUniverify({
	access_token,
	openid,
	inviteCode
})

参数说明

参数名 类型 必填 说明
access_token string 一键登录客户端返回的access_token
openid string 一键登录客户端返回的openid
inviteCode string 邀请码,仅注册时生效

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 微信登录

微信账号已存在时登录,否则注册

接口名:loginByWeixin

接口形式

await uniIdCo.loginByWeixin({
	code,
	inviteCode
})

参数说明

参数名 类型 必填 说明
code string uni.login接口返回的code参数
inviteCode string 邀请码,仅注册时生效

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 支持的登录方式:微信小程序、微信公众号、App、web站微信扫码登录
  • 微信登录会自动保存用户的openid,在uni-id-pages 1.0.8及更高版本在存储openid时会同时存储一份以当前应用的Appid(manifest.json内的DCloud AppId)为key的openid,见下方关于openid的说明。
  • 如果有多个应用同时使用微信小程序登录,且希望用户身份不隔离请确保这些应用在微信小程序平台为同一主体所有,即保证不同应用可以获取同样的unionid
  • uni-id-pages 1.0.8及以上版本会使用uni-open-bridge-common保存session_key(微信小程序登录)、access_token(微信公众号登录、微信App登录)这些信息,但是为了兼容旧版逻辑仍在用户表存储了一份副本。详细说明参考:自动保存用户sessionKey、accessToken等信息
    • 如果开发者在其他应用未使用 uni-open-bridge-common 管理 access_token 等信息,可能会造成 access_token 冲突。 关于openid的说明

uni-id-pages 1.0.7及之前的版本会将微信的openid存为如下格式

{
	"_id": "xx",
		"wx_openid": {
		"mp": "weixin-openid-demo"
	}
}

可以看到如果存在多个微信小程序应用连接一个uniCloud后台且关联同一个账号,此时只能存储一个小程序的openid。

uni-id-pages 1.0.8版本对此进行了调整修正,多个DCloud Appid可以对应不同的微信openid。以Appid__UNI_123456为例,openid会在数据库内存储为以下形式:

{
	"_id": "xx",
		"wx_openid": {
		"mp": "weixin-openid-demo",
		"mp___UNI_123456": "weixin-openid-demo",
	}
}

# QQ登录

QQ账号已存在时登录,否则注册

接口名:loginByQQ

接口形式

await uniIdCo.loginByQQ({
	code,
	accessToken,
	inviteCode
})

参数说明

参数名 类型 必填 说明
code string QQ小程序uni.login返回的code参数
accessToken string APP端QQ登录返回的accessToken参数
accessTokenExpired number accessToken过期时间,由APP端QQ登录返回的expires_in参数
inviteCode string 邀请码,仅注册时生效

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 支持的登录方式:QQ小程序、QQ App
  • QQ登录会自动保存用户的openid,在uni-id-pages 1.0.8及更高版本在存储openid时会同时存储一份以当前应用的Appid(manifest.json内的DCloud AppId)为key的openid,见下方关于openid的说明。
  • 如果有多个应用同时使用QQ小程序登录,且希望用户身份不隔离请确保这些应用在QQ小程序平台为同一主体所有,即保证不同应用可以获取同样的unionid
  • uni-id-pages 1.0.8及以上版本会使用uni-open-bridge-common保存session_key(QQ小程序登录)、access_token(QQ App登录)这些信息,但是为了兼容旧版逻辑仍在用户表存储了一份副本。详细说明参考:自动保存用户sessionKey、accessToken等信息

关于openid的说明

uni-id-pages 1.0.7及之前的版本会将QQ的openid以以下形式存储

{
	"_id": "xx",
		"qq_openid": {
		"mp": "weixin-openid-demo"
	}
}

可以看到如果存在多个QQ小程序关联同一个账号,这时候只能存储一个小程序的openid,在uni-id-pages 1.0.8版本对此进行了调整以Appid__UNI_123456为例,openid会在数据库内存储为以下形式

{
	"_id": "xx",
	"qq_openid": {
		"mp": "weixin-openid-demo",
		"mp___UNI_123456": "weixin-openid-demo",
	}
}

# 支付宝登录

支付宝账号已存在时登录,否则注册

接口名:loginByAlipay

接口形式

await uniIdCo.loginByAlipay({
	code,
	inviteCode
})

参数说明

参数名 类型 必填 说明
code string 支付宝小程序uni.login返回的code参数
inviteCode string 邀请码,仅注册时生效

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 苹果登录

苹果账号已存在时登录,否则注册

接口名:loginByApple

接口形式

await uniIdCo.loginByApple({
	identityToken,
	nickname,
	inviteCode
})

参数说明

参数名 类型 必填 说明
identityToken string App端苹果登录返回的identityTokenc参数
nickname string 昵称
inviteCode string 邀请码,仅注册时生效

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 登出

接口名:logout

接口形式

await uniIdCo.logout()

参数说明

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 注销

调用此接口后用户status将会设置为注销状态,需要注意的是目前token不会自动失效,后续会引入redis解决此问题。如果不需要此功能建议手动修改代码。

接口名:closeAccount

接口形式

await uniIdCo.closeAccount()

参数说明

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 获取支持的登录方式

接口名:getSupportedLoginType

接口形式

await uniIdCo.getSupportedLoginType()

参数说明

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
supportedLoginType array 支持的登录方式列表,见下方说明

supportedLoginType

登录方式 说明
username-password 用户名密码登录
mobile-password 手机号密码登录
email-password 邮箱密码登录
mobile-code 手机号验证码登录
univerify App一键登录
weixin 微信登录
qq QQ登录
apple 苹果登录
alipay 支付宝登录

# 绑定账号

# 使用短信验证码绑定手机号

接口名:bindMobileBySms

接口形式

await uniIdCo.bindMobileBySms({
	mobile,
	code,
	captcha
})

参数说明

参数名 类型 必填 说明
mobile string 手机号码
code string 短信验证码
captcha string 否(此操作错误3次以上为必填) 图形验证码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 使用一键登录绑定手机号

接口名:bindMobileByUniverify

接口形式

await uniIdCo.bindMobileByUniverify({
	openid,
	access_token
})

参数说明

参数名 类型 必填 说明
openid string 客户端一键登录返回的openid参数
access_token string 客户端一键登录返回的access_token参数

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 通过微信绑定手机号

使用此接口时务必注意

微信小程序对获取手机号的接口进行了安全升级,自 uni-id-co@1.0.25 以上版本开始,支持getPhoneNumber事件回调的动态口令code,同时为了向下兼容保留encryptedDataiv参数,建议开发者升级,以增强小程序安全性。

微信小程序的规则是客户端应先使用checkSession接口检测上次获取的sessionKey是否仍有效。

如果有效则直接使用上次存储的sessionKey即可,如果无效应重新调用login接口再次刷新sessionKey。

微信小程序登录、绑定小程序微信账号时会自动更新用户的sessionKey。

接口名:bindMobileByMpWeixin

接口形式

// uni-id-co >= 1.0.25
await uniIdCo.bindMobileByMpWeixin({
	code
})

// uni-id-co < 1.0.25
await uniIdCo.bindMobileByMpWeixin({
	encryptedData,
	iv
})

参数说明

参数名 类型 必填 说明
encryptedData string 微信小程序获取手机号返回的encryptedData参数
iv string 微信小程序获取手机号返回的iv参数
code string 微信小程序获取手机号返回的code参数; uni-id-co >= 1.0.25支持

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 绑定微信

接口名:bindWeixin

接口形式

await uniIdCo.bindWeixin({
	code
})

参数说明

参数名 类型 必填 说明
code string 微信登录返回的code参数

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken
  • 如果开发者在其他应用未使用 uni-open-bridge-common 管理 access_token 等信息,可能会造成 access_token 冲突。

# 绑定QQ

接口名:bindQQ

接口形式

await uniIdCo.bindQQ({
	code,
	accessToken
})

参数说明

参数名 类型 必填 说明
code string QQ小程序登录返回的code参数
accessToken string App端QQ登录返回的accessToken参数

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 绑定支付宝账号

接口名:bindAlipay

接口形式

await uniIdCo.bindAlipay({
	code
})

参数说明

参数名 类型 必填 说明
code string 支付宝小程序登录返回的code参数

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 绑定苹果账号

接口名:bindApple

接口形式

await uniIdCo.bindApple({
	identityToken
})

参数说明

参数名 类型 必填 说明
identityToken string 苹果登录返回的identityToken参数

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 解绑第三方账号uni-id-co 1.0.25+

如账号只有一个第三方登录方式时,需绑定手机号后在解绑。

# 解绑微信

接口名:unbindWeixin

接口形式

await uniIdCo.unbindWeixin()

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 解绑QQ

接口名:unbindQQ 接口形式

await uniIdCo.unbindQQ()

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 解绑支付宝

接口名:unbindAlipay 接口形式

await uniIdCo.unbindAlipay()

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 解绑苹果账号

接口名:unbindApple 接口形式

await uniIdCo.unbindApple()

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 用户信息

# 设置密码

接口名:setPwd

接口形式

await uniIdCo.setPwd({
	code,
	captcha,
	password
})

参数说明

参数名 类型 必填 说明
code string 手机验证码
captcha string 否(此操作错误3次以上为必填) 图形验证码
password string 密码

返回值

参数名 类型 说明
errCode string|number 错误码

# 修改密码

接口名:updatePwd

接口形式

await uniIdCo.updatePwd({
	oldPassword,
	newPassword
})

参数说明

参数名 类型 必填 说明
oldPassword string 旧密码
newPassword string 新密码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 通过短信验证码重置密码

接口名:resetPwdBySms

接口形式

await uniIdCo.resetPwdBySms({
	mobile,
	code,
	password,
	captcha
})

参数说明

参数名 类型 必填 说明
mobile string 手机号
code string 短信验证码
password string 密码
captcha string 否(此操作错误3次以上为必填) 图形验证码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 通过邮箱验证码重置密码

接口名:resetPwdByEmail

接口形式

await uniIdCo.resetPwdByEmail({
	email,
	code,
	password,
	captcha
})

参数说明

参数名 类型 必填 说明
email string 邮箱
code string 邮箱验证码
password string 密码
captcha string 否(此操作错误3次以上为必填) 图形验证码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 获取账户简略信息

接口名:getAccountInfo

接口形式

await uniIdCo.getAccountInfo()

参数说明

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
isUsernameSet boolean 是否已有用户名
isNicknameSet boolean 是否已有昵称
isPasswordSet boolean 是否已设置密码
isMobileBound boolean 手机号是否已绑定
isEmailBound boolean 邮箱是否已绑定
isWeixinBound boolean 微信是否已绑定
isQQBound boolean QQ是否已绑定
isAlipayBound boolean 支付宝是否已绑定
isAppleBound boolean 苹果账号是否已绑定

# 接受邀请

接口名:acceptInvite

接口形式

await uniIdCo.acceptInvite({
	inviteCode
})

参数说明

参数名 类型 必填 说明
inviteCode string 邀请码

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 获取邀请的用户

接口名:getInvitedUser

接口形式

await uniIdCo.getInvitedUser({
	level,
	limit,
	offset,
	needTotal
})

参数说明

参数名 类型 必填 说明
level number 邀请用户层级,例,值为1时表示自己直接邀请的用户
limit number 本次请求返回的数量,默认:20
offset number 列表偏移数值,默认:0,通过和limit结合进行分页
needTotal boolean 是否返回总数,默认:false

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 仅在用户token即将过期时返回新newToken

# 获取账户实名信息(脱敏)uni-id-pages 1.1.2+

接口名:getRealNameInfo

接口形式

await uniIdCo.getRealNameInfo({
	decryptData
})

参数说明

参数名 类型 必填 说明
decryptData boolean 是否解密数据;默认:true

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
type number 用户类型:0 个人用户 1 企业用户
authStatus number 认证状态:0 未认证 1 等待认证 2 认证通过 3 认证失败
realName string 姓名(脱敏);用户类型为 0 时返回
identity string 身份证号码(脱敏);用户类型为 0 时返回

注意

在uni-id-pages中默认启用敏感数据加解密,如果开发者没有使用uni-id提供的敏感信息加密功能,请将decryptData参数改为false,返回原始信息

# 安全验证

# 创建图形验证码

接口名:createCaptcha

接口形式

await uniIdCo.createCaptcha({
	scene
})

参数说明

参数名 类型 必填 说明
scene string 图形验证码使用场景,务必确保使用验证码的场景和生成验证码时传的场景参数相匹配,否则会校验不通过,参考:图形验证码场景

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
captchaBase64 string 验证码:base64 格式

# 刷新图形验证码

接口名:refreshCaptcha

接口形式

await uniIdCo.refreshCaptcha({
	scene
})

参数说明

参数名 类型 必填 说明
scene string 图形验证码使用场景,参考:图形验证码场景

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
captchaBase64 string 验证码:base64 格式

# 发送短信

发送短信功能需要以下前置工作

  1. 开发者中心开通短信服务,并申请短信模板
  2. 在uni-id的配置文件里面添加验证码使用场景对应的短信模板信息,参考:uni-id配置文件

接口名:sendSmsCode

接口形式

await uniIdCo.sendSmsCode({
	mobile,
	captcha,
	scene
})

参数说明

参数名 类型 必填 说明
mobile string 手机号码
captcha string 图形验证码
scene string 短信验证码使用场景,务必确保使用验证码的场景和发送验证码时传的场景参数相匹配,否则会校验不通过,参考:短信验证码使用场景

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 发送邮箱验证码

接口名:sendEmailCode

接口形式

await uniIdCo.sendEmailCode({
	email,
	captcha,
	scene
})

参数说明

参数名 类型 必填 说明
email string 邮箱
captcha string 图形验证码
scene string 使用场景,参考:手机、邮箱验证码使用场景

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 其他用户端接口

# 刷新token

接口名:refreshToken

接口形式

await uniIdCo.refreshToken()

参数说明

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 更新/设置uni-push clientId

如未使用uni-push 2.0无需关注此接口。此接口用于更新uni-id-device表的unipush_clientid字段,用于按客户端、用户等维度推送消息

接口名:setPushCid

接口形式

await uniIdCo.setPushCid({
	pushClientId
})

参数说明

参数名 类型 必填 说明
pushClientId string 客户端获取的uni-push对应的clientId

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

# 微信安全网络握手

此接口用于微信小程序端安全网络的握手,安全网络相关文档请参考:安全网络

一般无需通过uniCloud.importObject方式调用,在客户端调用uniCloud.initSecureNetworkByWeixin()时会通过此接口获取会话相关信息。

此接口会将会话信息存储在opendb-open-data表内,如果在initSecureNetworkByWeixin方法内传递了callLoginByWeixin: true会在存储会话信息的同时执行一次uni-id-co的loginByWeixin方法

# 管理接口

# 管理员新增用户

接口名:addUser

接口形式

await uniIdCo.addUser({
	username,
	password,
	authorizedApp,
	nickname,
	role
})

参数说明

参数名 类型 必填 说明
username string 用户名
password string 密码
authorizedApp Array<string> 允许登录的app列表
nickname string 昵称
role Array<string> 用户角色
mobile string 手机号
email string 邮箱
tags array 用户标签
status number 用户状态,参考:用户状态

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 管理员修改用户

接口名:updateUser

接口形式

await uniIdCo.updateUser({
	uid,
	username,
	password,
	nickname,
	authorizedApp,
	role,
	mobile,
	email,
	tags,
	status
})

参数说明

参数名 类型 必填 说明
uid string 要更新的用户id
username string 用户名
password string 密码
nickname string 昵称
authorizedApp Array<string> 允许登录的app列表
role Array<string> 用户角色
mobile string 手机号
email string 邮箱
tags array 用户标签
status number 用户状态,参考:用户状态

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 授权用户登录指定客户端

接口名:authorizeAppLogin

接口形式

await uniIdCo.authorizeAppLogin({
	uid,
	appId
})

参数说明

参数名 类型 必填 说明
uid string 用户id
appId string 允许登录应用的DCloud AppId

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 此接口为管理端接口
  • 仅在用户token即将过期时返回新newToken

# 移除用户登录授权

接口名:removeAuthorizedApp

接口形式

await uniIdCo.removeAuthorizedApp({
	uid,
	appId
})

参数说明

参数名 类型 必填 说明
uid string 用户id
appId string 禁止登录的应用的DCloud AppId

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 此接口为管理端接口
  • 仅在用户token即将过期时返回新newToken

# 设置允许登录的应用列表

接口名:setAuthorizedApp

接口形式

await uniIdCo.setAuthorizedApp({
	uid,
	appIdList
})

参数说明

参数名 类型 必填 说明
uid string 用户id
appIdList Array<String> 允许登录的应用对应的DCloud AppId列表

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间

注意

  • 此接口为管理端接口
  • 仅在用户token即将过期时返回新newToken

# 外部系统联登

适合自己有用户系统,同时需要使用依赖UniId的业务,将自身系统的用户账号导入uniId,为其创建一个对应uniId的账号,使得该账号可以使用依赖uniId的系统及功能。 由于此方案的接口不需要密码验证,开发者务必要保证接口只能在服务端调用,同时要求在请求时计算签名来保证安全。

联登相关接口只支持HTTP方式调用,调用时需要携带鉴权签名值,查看URL化请求鉴权签名计算

# 注册用户

外部用户注册,注册成功后,uni-id 返回 uid 与 用户 token ,请务必在自身系统中维护好 uid 与 token。

接口地址

POST /your-uni-id-co-path/externalRegister

HTTP 示例

POST /your-uni-id-co-path/externalRegister HTTP/1.1
Host: xxx.com
uni-id-nonce: xxxxxxx
uni-id-timestamp: 1676882808550
uni-id-signature: 11c965267a4a02c6978949c7135215b0a75aea22b2b84ed491e792365c8269efa
Content-Type: application/json
Cache-Control: no-cache

{"externalUid": "test externalUid", "nickname": "张三", "avatar": "xxxxxxx", "gender": 0}

注意

Request Body 说明

参数名 类型 必填 说明
externalUid string 自身系统的用户id,必须保证唯一性。
nickname string 用户昵称
avatar string 用户头像
gender number 用户性别;0 未知 1 男性 2 女性

Response Body 说明

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间
externalUid string 自身系统的用户id
uid string uni-id体系的用户Id
nickname string 用户昵称
avatar string 用户头像
gender string 用户性别;0 未知 1 男性 2 女性

# 用户登录

外部用户登录,用于获取用户token

接口地址

POST /your-uni-id-co-path/externalLogin

HTTP 示例

POST /your-uni-id-co-path/externalLogin HTTP/1.1
Host: xxx.com
uni-id-nonce: xxxxxxx
uni-id-timestamp: 1676882808550
uni-id-signature: 11c965267a4a02c6978949c7135215b0a75aea22b2b84ed491e792365c8269efa
Content-Type: application/json
Cache-Control: no-cache

{"externalUid": "test externalUid"}

注意

Request Body 说明

参数名 类型 必填 说明
uid string uni-id体系的用户Id;与externalUid 二选一
externalUid string 自身系统的用户id;与 uid 二选一

Response Body 说明

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
newToken object token信息
 |- token string token
 |- tokenExpired string token过期时间
uid string uni-id体系的用户Id

# 修改用户信息

外部用户修改账号信息,如用户在自身系统内修改了用户信息后,通过此接口同步修改uni-id中用户信息。

接口地址

POST /your-uni-id-co-path/updateUserInfoByExternal

HTTP 示例

POST /your-uni-id-co-path/updateUserInfoByExternal HTTP/1.1
Host: xxx.com
uni-id-nonce: xxxxxxx
uni-id-timestamp: 1676882808550
uni-id-signature: 11c965267a4a02c6978949c7135215b0a75aea22b2b84ed491e792365c8269efa
Content-Type: application/json
Cache-Control: no-cache

{"externalUid": "test externalUid", "nickname": "张三"}

注意

Request Body 说明

参数名 类型 必填 说明
uid string uni-id体系的用户Id;与externalUid 二选一
externalUid string 自身系统的用户id;与 uid 二选一
username string 用户名
password string 密码
nickname string 昵称
authorizedApp Array<string> 允许登录的app列表
role Array<string> 用户角色
mobile string 手机号
email string 邮箱
tags array 用户标签
status number 用户状态,参考:用户状态
avatar string 用户头像
gender number 用户性别;0 未知 1 男性 2 女性

Response Body 说明

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息

# 实名认证 uni-id-pages 1.1.7+

实人认证相关功能建议或问题,可以加入uni-im交流群进行讨论,点此加入

基于实人认证服务实现,实现用户刷脸核验真实身份,完成实名认证。 uni-id-pages已内置实人认证前端页面与云端云对象,了解如在uni-id-pages中使用。

# 获取认证ID

接口名:getFrvCertifyId

接口形式

await uniIdCo.getFrvCertifyId({
	realName,
	idCard,
	metaInfo
})

参数说明

参数名 类型 必填 说明
realName string 用户真实姓名
idCard string 用户身份证号码
metaInfo String 客户端初始化时返回的metaInfo

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
certifyId object 认证Id;用于客户端调用认证接口及云函数获取认证结果

# 获取认证结果

接口名:getFrvAuthResult

接口形式

await uniIdCo.getFrvAuthResult({
	certifyId
})

参数说明

参数名 类型 必填 说明
certifyId string 认证Id

返回值

参数名 类型 说明
errCode string|number 错误码
errMsg string 错误信息
authStatus number 认证状态:0 未认证 1 等待认证 2 认证通过 3 认证失败
realName string 姓名(脱敏);敏感信息加密参考
identity string 身份证号码(脱敏);敏感信息加密参考

# 其他功能

# 覆盖或新增校验规则

uni-id-co将validator实例挂载在云对象的this上,在uni-id-co/index.obj.js获取validator实例后可以使用validator实例的mixin方法覆盖或者新增校验规则。

接口形式:validator.mixin(String type, Function handler)。其中type为规则类型(字符串),handler为校验函数

// uni-id-co/index.obj.js
const {
	Validator
} = require('./common/validator.js')
module.exports = {
	_before () {
		this.validator = new Validator()
		/**
		 * 示例:覆盖密码验证规则
		 */
		this.validator.mixin('password', function (password) {
			if (typeof password !== 'string' || password.length < 10) {
				// 调整为密码长度不能小于10
				return {
					errCode: ERROR.INVALID_PASSWORD
				}
			}
		})
		/**
		 * 示例:新增验证规则
		 */
		this.validator.mixin('timestamp', function (timestamp) {
			if (typeof timestamp !== 'number' || timestamp > Date.now()) {
				return {
					errCode: ERROR.INVALID_PARAM
				}
			}
		})
		// // 新增规则同样可以在数组验证规则中使用
		this.validator.valdate({
			timestamp: 123456789
		}, {
			timestamp: 'timestamp'
		})
		this.validator.valdate({
			timestampList: [123456789, 123123123123]
		}, {
			timestampList: 'array<timestamp>'
		})
		// // 甚至更复杂的写法
		this.validator.valdate({
			timestamp: [123456789, 123123123123]
		}, {
			timestamp: 'timestamp|array<timestamp>'
		})
	}
}

# 登录服务开通与配置

服务端uni-id的密钥信息统一在uni-config-center中配置,路径:uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json 以下简称:uni-id配置文件,完整的配置信息详情查看

# 一键登录

一键登录是运营商提供的、比短信验证码更方便、更安全、更便宜的方案。详见

  • 使用本功能需要在DCloud开发者中心 -> 开通并充值
  • 模块配置:manifest.json-->App模块配置 -->OAuth(登录鉴权)-->一键登录,点击后面的开通配置,在随后打开的web界面中,同意协议,并点击充值按钮充值。如只是测试,可以只充值1元钱。如果你已经确定包名,则可以在web界面点击“添加应用”,提交审核。这个是正式打包必须的。真机运行可以跳过此环节。

# 微信登录

uni-id-pages已全面支持:app、小程序、web(uni-id-pages 版本号1.0.8起),三端的微信登录。

微信将应用分为4类:

  1. 移动应用:指非微信的App,调用微信登陆。属于微信开放平台微信开放平台账号
  2. 网站应用:指微信外的浏览器网页,通过手机微信扫码等方式调用微信登录。属于微信开放平台微信开放平台账号
  3. 公众帐号:指在微信内置浏览器访问公众号H5。属于微信公众号平台https://mp.weixin.qq.com/
  4. 小程序:指微信内的小程序。属于微信公众号平台https://mp.weixin.qq.com/

这里的网站应用公众帐号都是给web应用,接入微信登录功能。但有如下区别:

宿主环境 所属类型 登录方式
微信APP 公众帐号 网页授权登录
普通浏览器 网站应用 手机微信扫码登录

# 如何选择微信开放平台和微信公众平台

  • 如果你的应用只运行在微信内,不管是微信小程序还是微信公众号H5,都属于微信公众平台
  • 如果你的应用运行在微信外,不管是其他App还是微信外的其他网页,都属于微信开放平台

根据你的需求开通相应平台账户即可。

注意

如果你的应用有多端,实现同一个用户在公众号、小程序、APP、官方网站等不同场景里的身份统一识别、信息同步和行为跟踪 (详情参考:“UnionID关联”功能介绍及运营建议) 就需要将小程序公众帐号绑定到同一个微信开放平台账号下。

  • 绑定方式:登录微信开放平台 -> 管理中心 -> 选择公众号/小程序 -> 点击绑定公众号/小程序

# 客户端配置

  • APP端:
  • 小程序端:打开manifest.json -> 微信小程序配置 -> 填写微信小程序AppID
  • web端:打开/uni_modules/uni-id-pages/config.js -> appid -> weixinh5节点配置微信公众号的appid,web节点配置微信开放平台创建的网站应用appid

# 服务端配置

  • 服务端uni-id的密钥信息统一在uni-config-center中配置,路径:uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json,完整的配置信息详情查看

# web端微信登录专题

登录的流程为:

  1. 应用页面,打开微信登录授权页链接(以get参数的方式传递appid和redirect_uri)
  2. 进入授权页面,用户同意授权得到code;以get参数的形式携带code,重定向至步骤1填写的redirect_uri
  3. 回到应用页面,拿到code值调用uni-id-co云对象的loginByWeiXin方法,得到token完成登录
  • appid说明:微信app内打开的网页,为公众号的appid。其他场景则为在微信开放平台创建的网站应用的appid。
  • redirect_uri说明:进入授权页面后返回的网站链接,此链接的域名需要先在服务后台配置,详情查看:回调域名的配置

示例代码已经在uni-id-pages插件中提供。

# 回调域名的配置

  • 手机微信扫码登录 微信开放平台 -> 管理中心 -> 网站应用 -> 选择对应的应用名称,点击查看 -> 开发信息,点击修改 -> 填写授权回调域

  • 基于微信公众号auth登录 登录微信公众号 -> 设置与开发 -> 公众号设置 -> 设置网页授权域名

# 本地调试

回调域名,必须接入外网已经备案的URL地址,不然本地没法进行调试,你可以做内网穿透,映射生成一个外网URL地址来进行回调测试。但是那样比较麻烦,这里我们介绍一种基于HBuilderX本地启动一个Web Server进行调试的方法。

  1. 启动一个80端口的Web Server服务
  • 打开manifest.json -> H5配置 -> 端口 -> 输入80

注意:mac系统中,非root用户是无法使用小于1024的常用端口的。解决方案打开终端,cd 到 HBuilderX安装目录(默认是Applications目录,执行cd /Applications),然后执行 sudo ./HBuilderX.app/Contents/MacOS/HBuilderX 输入开机密码,再按回车,此时会以root用户权限重新打开HBuilderX;

  • 通过HBuilderX运行项目到浏览器,即可启用一个80端口的Web Server了

如果没有启动80端口而是81等,说明你的端口被占用了。你有两个办法1.关闭可疑程序,或直接重启电脑 2.命令行关闭占用的端口详情查看

  1. 实现访问域名直接指向你的本地web Server 可以通过内网穿透实现,但比较麻烦且可能会影响线上用户。这里推荐直接修改hosts,hosts是一个没有扩展名的系统文件,其基本作用就是将一些常用的网址域名与其对应的 IP 地址建立一个关联“ 数据库 ”。当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从hosts文件中寻找对应的 IP 地址,一旦找到,系统就会立即打开对应网页,如果没有找到,系统才会将网址提交 DNS 域名解析服务器进行 IP 地址的解析。 host文件路径: Windows系统一般为:C:\Windows\System32\drivers\etc。mac系统:/etc/ 用HBuilderX打开hosts文件,在末尾添加一行 127.0.0.1 你的域名保存即可。 此时访问域名,如果就能看到和你的项目运行到浏览器一样的效果,说明已经成功了。

# 苹果登录集成指南

  • 模块配置:manifest.json --> App模块配置 --> OAuth(登录鉴权)勾选苹果登录IOS苹果授权登录参考文档。如不发布到Appstore,不需要配置此项
  • uni-id配置:uni-id配置文件 --> app --> oauth --> apple 填写bundleId
  • 关联域配置:manifest.json --> App常用其他设置 --> iOS设置 --> 关联域(Associated Domains) 填写配置 参考教程。如不发布到Appstore,不需要配置此项

# 短信验证码

为了方便开发调试,uni-id-pages未配置短信登录时,自动启动测试模式;直接使用:123456作为短信验证码即可。

# 从老版uni-id公共模块升级到uni-id-pages

在HBuilderX 3.5之前,DCloud提供了一个公共模块uni-id(注意别和uni-id-common混淆)和一个示例性云函数uni-id-cf(集成在uni-starter和uni-admin中)。

老的公共模块uni-id是一个大而全的账户管理公共模块,体积太大,不适合被其他云函数引用。比如某个业务云函数需要校验用户token,引用的uni-id公共模块还包含了忘记密码的代码,很浪费资源。

从HBuilder 3.5起,uni-id和uni-id-cf都将被淘汰,不再更新。老的公共模块uni-id被拆开,变成了uni-id-common公共模块和uni-id-co云对象。

uni-id-common很精简,只包括token和权限,适合被所有云函数引用。

uni-id-co则是一个更加比uni-id-cf更完善和规范的用户管理的云对象。

然后DCloud推出了uni-id-pages

目前uni-starter和uni-admin仍然使用老版uni-id和uni-id-cf,官方正在升级中,将其中的用户管理升级为uni-id-pages。

# 自uni-id公共模块升级到uni-id-common + uni-id-co

此次升级做了大幅改动,多数接口自公共模块中移除,改为由uni-id-co实现。仅创建token、刷新token、校验token接口保留在uni-id公共模块内。除接口调整外,uni-id体系(包含uni-id公共模块、uni-id-co)还有以下调整:

  • 彻底移除bindTokenToDevice逻辑及配置
  • 彻底移除removePermissionAndRoleFromToken逻辑及其配置,token内一定会缓存用户角色权限
  • uni-id-common 1.0.9及更低版本未将token存储在用户表内,自uni-id-common 1.0.10起仍将token存储在用户表内(与旧版本uni-id保持一致)
  • 为用户添加valid_token_date字段,修改密码、重置密码、封禁用户场景下更新此值,刷新token时进行验证,如果token创建时间小于此时间戳,将等同于token已过期
  • 为用户添加third_party字段,用于缓存用户在三方平台的token、sessionKey等信息
  • 用户名、邮箱入库时会转化为全小写
  • preferedAppPlatformpreferedWebPlatform不再生效,uni-id内部会使用app代替app-plus、使用web代替h5
  • wx_openid字段下平台名称调整,详见下方字段名调整相关说明
  • qq_openid字段下平台名称调整,详见下方字段名调整相关说明

# 字段名调整

uni-id升级为uni-id-co + uni-id-common需要对个别字段进行重命名,直接运行下面的云函数即可重命名改动的字段。另外还需要修改一下索引,删除旧字段的索引,增加新字段的索引。建议在凌晨或其他低峰段操作,避免数据库操作耗时过久影响线上用户,如果有停服逻辑也可对系统短暂停服再操作。(在实际数据测试中,50000条用户记录重命名字段耗时约5秒钟)

'use strict';
const db = uniCloud.database()
const dbCmd = db.command
exports.main = async (event, context) => {
	await db.collection('uni-id-users').update({
		wx_openid: {
			'app-plus': dbCmd.rename('wx_openid.app'), // app端微信openid
			'mp-weixin': dbCmd.rename('wx_openid.mp'), // 小程序端微信openid
			'h5-weixin': dbCmd.rename('wx_openid.h5'), // 微信公众号端微信openid
			'h5-web': dbCmd.rename('wx_openid.web') // web端微信openid
		},
		qq_openid: {
			'app-plus': dbCmd.rename('qq_openid.app'), // app端QQ openid
			'mp-qq': dbCmd.rename('qq_openid.mp') // 小程序端QQ openid
		}
	})
	return {}
};

# URL化请求鉴权签名

uni-id-co@1.1.10及以上版本支持使用uni-cloud-s2s进行请求签名验证,uni-cloud-s2s使用方式详见

uni-id-co请求鉴权签名与uni-cloud-s2s不能同时存在,如果存在uni-cloud-s2s,则会优先使用uni-cloud-s2s进行请求签名验证

uni-id-co 在URL化请求时,会对以下 API 进行调用鉴权验证, 在调用 API 时,开发者需要使用请求鉴权密钥(详见配置文件requestAuthSecret按照 uni-id 的约定方式对请求中的关键数据进行签名值计算, 并将签名值添加到Header请求头的 uni-id-signature 参数中传给 uni-id 进行签名验证,uni-id 会对接收到数据进行签名值计算, 并与接收到的请求签名值进行比对,如果签名值不一致,则视为无效签名,将拒绝本次请求。

需要进行签名的API列表

API
externalRegister
externalLogin

# 请求头公共参数

参数名称 类型 是否必须 描述
uni-id-nonce string 随机字符串
uni-id-timestamp string 当前时间戳; 单位毫秒
uni-id-signature string 请求鉴权签名; 签名算法见下

# 鉴权签名算法

如下为某请求体参数,介绍如何进行签名:

{
	"clientInfo": {
		"appId": "__test__"
	},
	"uniIdToken": "xxxxxx",
	"params": {
		"foo": 1,
		"bar": 2,
		"foo_bar": 3,
		"foobar": 4
	}
}
  1. 将API请求参数(只包括请求体(body)中的params参数,但除去array与object类型的参数),根据参数名称的ASCII码表的顺序排序。如:foo:1, bar:2, foo_bar:3, foobar:4排序后的顺序是 bar:2, foo:1, foo_bar:3, foobar:4
  2. 将排序好的参数名和参数值按照 key1=value1&key2=value2 格式拼装在一起,根据上面的示例得到的结果为:bar=2&foo=1&foo_bar=3&foobar=4
  3. 把拼装好的字符串采用utf-8编码,开发者使用请求鉴权密钥与随机串对时间戳与待签名字符串进行 HmacSHA256 加密处理,计算得出请求签名值,如:HmacSHA256(timestamp + bar=2&foo=1&foo_bar=3&foobar=4, requestAuthSecret + nonce)
  4. 将加密得到的二进制结果使用十六进制表示,值必须为大写,如:Hex.stringify(Utf8.parse("helloworld")) = "68656C6C6F776F726C64"

# 签名值计算示例

# Nodejs

const crypto = require('crypto')

class Sign {
	constructor (requestAuthSecret) {
		this.requestAuthSecret = requestAuthSecret
	}

	getSignature (params, nonce, timestamp) {
		const paramsStr = this.getParamsString(params)
		const signature = crypto.createHmac('sha256', `${requestAuthSecret}${nonce}`).update(`${timestamp}${paramsStr}`).digest('hex')

		return signature.toUpperCase()
	}

	getParamsString (params) {
		return Object.keys(params)
				.sort()
				.filter(item => typeof params[item] !== "object")
				.map(item => `${item}=${params[item]}`)
				.join('&')
	}
}

const requestAuthSecret = "testSecret"
const nonce = Math.random().toString(36).substring(2)
const timestamp = Date.now()

const params = {
	foo: 1,
	bar: 2,
	foo_bar: 3,
	foobar: 4
}

const sign = new Sign(requestAuthSecret)
const signature = sign.getSignature(params, nonce, timestamp)

console.log("nonce: ", nonce)
console.log("timestamp: ", timestamp)
console.log("signature: ", signature)

# PHP

<?php

class Sign {
    private $requestAuthSecret;

    public function __construct ($requestAuthSecret) {
        $this->requestAuthSecret = $requestAuthSecret;
    }

    public function getSignature ($params, $nonce, $timestamp) {
        $paramsStr = $this->getParamsString($params);
        $signature = hash_hmac('sha256', ((string)$timestamp . $paramsStr), ($this->requestAuthSecret . $nonce));

        return strtoupper($signature);
    }

    private function getParamsString ($params) {
        ksort($params);

        $paramsStr = [];
        foreach($params as $key=>$value){
            if (gettype($value) == "array" || gettype($value) == "object") {
                continue;
            }

            array_push($paramsStr, $key . '=' . $value);
        }

        return join('&', $paramsStr);
    }
}

$requestAuthSecret = "testSecret";
$nonce = sprintf("%d", rand());
$timestamp = time() * 1000;

$params = [
    "foo" => 1,
    "bar" => 2,
    "foobar" => 4,
    "foo_bar" => 3,
];

$sign = new Sign($requestAuthSecret);
$signature = $sign->getSignature($params, $nonce, $timestamp);

print_r("nonce: " . $nonce . PHP_EOL);
print_r("timestamp: " . $timestamp . PHP_EOL);
print_r("signature: " . $signature);

# Python

import hmac
import hashlib
import time

class Sign:
  def __init__(self, requestAuthSecret):
        self.requestAuthSecret = requestAuthSecret

  def get_signature (self, params, nonce, timestamp):
    params_str = self.get_params_string(params)
    signature = hmac.new(bytes("%s%s" % (self.requestAuthSecret, nonce), 'utf-8'), bytes("%s%s" % (timestamp, params_str), 'utf-8'), digestmod = hashlib.sha256).hexdigest().upper()

    return signature

  def get_params_string(self, params):
    params_str = []
    for k in sorted(params):
      if isinstance(params[k], (list, dict)):
        continue
      params_str.append("%s=%s" % (k, params[k]))

    return "&".join(params_str)

if __name__ == "__main__":
  requestAuthSecret = "testSecret"
  nonce = "xxxxxxx"
  timestamp = int(round(time.time() * 1000))

  params = {
    "foo": 1,
    "bar": 2,
    "foobar": 4,
    "foo_bar": 3,
  }

  sign = Sign(requestAuthSecret)
  signature = sign.get_signature(params, nonce, timestamp)

  print(nonce, timestamp, signature)

# Go

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"time"
)

type Sign struct {
	requestAuthSecret string
}

func (sign Sign) getSignature(params map[string]string, nonce string, timestamp int64) string {
	paramsStr := sign.getParamsString(params)

	mac := hmac.New(sha256.New, []byte(sign.requestAuthSecret+nonce))
	mac.Write([]byte(strconv.Itoa(int(timestamp)) + paramsStr))
	hexSignature := mac.Sum(nil)
	signature := hex.EncodeToString(hexSignature)

	return strings.ToUpper(signature)
}

func (sign Sign) getParamsString(params map[string]string) string {
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}

	sort.Strings(keys)

	var paramsStr string = ""
	i := 0
	for _, k := range keys {
		v := params[k]
		if i != 0 {
			paramsStr += "&"
		}
		paramsStr += k
		paramsStr += "="
		paramsStr += v
		i++
	}

	return paramsStr
}

func main() {
	requestAuthSecret := "testSecret"
	nonce := "xxxxxxx"
	timestamp := time.Now().UnixMilli()

	params := map[string]string{
		"foo":     "1",
		"bar":     "2",
		"foo_bar": "3",
		"foobar":  "4",
	}

	sign := Sign{
		requestAuthSecret: requestAuthSecret,
	}
	signature := sign.getSignature(params, nonce, timestamp)

	result := fmt.Sprintf("nonce: %s, timestamp: %d, signature: %s", nonce, timestamp, signature)

	fmt.Println(result)
}

# Java

import java.util.HashMap;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Arrays;
import java.util.Map;

public class Sign {
    static String requestAuthSecret = "testSecret";

    public static void main(String[] args) {
        String nonce = "xxxxxx";
        long timestamp = System.currentTimeMillis();

        Map<String, String> params = new HashMap<String, String>();

        params.put("foo", "1");
        params.put("bar", "2");
        params.put("foo_bar", "4");
        params.put("foobar", "3");

        String signature = getSignature(params, nonce, timestamp);

        System.out.println("nonce: " + nonce);
        System.out.println("timestamp: " + timestamp);
        System.out.println("signature: " + signature);
    }

    public static String getSignature (Map<String, String> params, String nonce, long timestamp) {
        String paramsStr = getParamsString(params);
        String algorithm = "HmacSHA256";
        Mac hmacSha256;
        String digestHexString = null;

        try {
            hmacSha256 = Mac.getInstance(algorithm);

            String key = new StringBuilder().append(requestAuthSecret).append(nonce).toString();
            String message = new StringBuilder().append(Long.toString(timestamp)).append(paramsStr).toString();

            byte[] keyBytes = key.getBytes("utf-8");
            byte[] messageBytes = message.getBytes("utf-8");

            hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, algorithm));
            byte[] digestBytes = hmacSha256.doFinal(messageBytes);

            digestHexString = byteArrayToHexString(digestBytes);
        } catch (Exception e) {
            System.out.println("[ERROR] not supposed to happen: " + e.getMessage());
        }

        return digestHexString.toUpperCase();
    }

    private static String getParamsString (Map<String, String> params) {

        String[] keys = params.keySet().toArray(new String[0]);
        Arrays.sort(keys);

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < keys.length; i ++) {
            String key = keys[i];

            if (i != 0) {
                sb.append("&");
            }

            sb.append(key).append("=").append(params.get(key));
        }

        return sb.toString();
    }

    private static String byteArrayToHexString(byte[] b) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; b!=null && n < b.length; n++) {
            stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString();
    }
}
本页导读