From b378861b5d365c27e0ba6d9d789234305cf4fe2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E4=BA=9A=E7=90=AA?= Date: Wed, 22 Jun 2022 19:29:40 +0800 Subject: [PATCH] docs: uni-id hx 3.5.0 --- docs/collocation/pages.md | 1 + docs/uniCloud/_sidebar.md | 5 +- docs/uniCloud/client-sdk.md | 138 ++- docs/uniCloud/uni-id-common.md | 158 +++ docs/uniCloud/uni-id-pages.md | 1992 +++++++++++++++++++++++++++++++ docs/uniCloud/uni-id-summary.md | 1033 ++++++++++++++++ docs/uniCloud/uni-id.md | 2 + 7 files changed, 3315 insertions(+), 14 deletions(-) create mode 100644 docs/uniCloud/uni-id-common.md create mode 100644 docs/uniCloud/uni-id-pages.md create mode 100644 docs/uniCloud/uni-id-summary.md diff --git a/docs/collocation/pages.md b/docs/collocation/pages.md index 342d0b339..ffc02b65c 100644 --- a/docs/collocation/pages.md +++ b/docs/collocation/pages.md @@ -17,6 +17,7 @@ |[leftWindow](/collocation/pages?id=leftwindow)|Object|否|大屏左侧窗口|H5| |[topWindow](/collocation/pages?id=topwindow)|Object|否|大屏顶部窗口|H5| |[rightWindow](/collocation/pages?id=rightwindow)|Object|否|大屏右侧窗口|H5| +|[uniIDRouter](/uniCloud/uni-id-summary.md?id=uni-id-router)|Object|否|自动跳转相关配置,新增于HBuilderX 3.5.0|| 以下是一个包含了所有配置选项的 `pages.json` : diff --git a/docs/uniCloud/_sidebar.md b/docs/uniCloud/_sidebar.md index 7f96669a0..8af807f10 100644 --- a/docs/uniCloud/_sidebar.md +++ b/docs/uniCloud/_sidebar.md @@ -38,7 +38,10 @@ * [云存储API](uniCloud/storage.md) * [腾讯云自定义登录](uniCloud/authentication.md) * [腾讯云权限管理](uniCloud/policy-tcb.md) -* [uni-id用户体系](uniCloud/uni-id.md) +* uni-id用户体系 + * [综述](uniCloud/uni-id-summary.md) + * [uni-id-common](uniCloud/uni-id-common.md) + * [uni-id-pages](uniCloud/uni-id-pages.md) * [uni一键登录](uniCloud/univerify.md) * [发送短信](uniCloud/send-sms.md) * [前端网页托管](uniCloud/hosting.md) diff --git a/docs/uniCloud/client-sdk.md b/docs/uniCloud/client-sdk.md index 4abbe10ad..b89dd9a11 100644 --- a/docs/uniCloud/client-sdk.md +++ b/docs/uniCloud/client-sdk.md @@ -7,19 +7,24 @@ uniCloud分为客户端和云端两部分,有些接口名称相同,参数也 客户端API列表 -|API |描述 | -|-- |-- | -|uniCloud.callFunction() |客户端调用云函数 [详情](https://uniapp.dcloud.net.cn/uniCloud/cf-functions?id=clientcallfunction) | -|uniCloud.database() |客户端访问云数据库,获取云数据库对象引用 [详情](https://uniapp.dcloud.net.cn/uniCloud/clientdb) | -|uniCloud.uploadFile() |客户端直接上传文件到云存储 [详情](https://uniapp.dcloud.net.cn/uniCloud/storage?id=uploadfile) | -|uniCloud.getTempFileURL() |客户端获取云存储文件的临时路径 [详情](https://uniapp.dcloud.net.cn/uniCloud/storage?id=gettempfileurl) | -|uniCloud.chooseAndUploadFile() |客户端选择文件并上传 [详情](https://uniapp.dcloud.net.cn/uniCloud/storage?id=chooseanduploadfile) | -|uniCloud.getCurrentUserInfo() |获取当前用户信息 [详情](#client-getcurrentuserinfo) | -|uniCloud.init() |同时使用多个服务空间时初始化额外服务空间 [详情](https://uniapp.dcloud.net.cn/uniCloud/init) | -|uniCloud.addInterceptor() |新增拦截器 [详情](#add-interceptor) | -|uniCloud.removeInterceptor() |移除拦截器 [详情](#remove-interceptor) | -|uniCloud.onResponse() |监听服务端(云函数、云对象、clientDB)响应 [详情](#on-response) | -|uniCloud.offResponse() |移除监听服务端(云函数、云对象、clientDB)响应 [详情](#off-response) | +|API |描述 | +|-- |-- | +|uniCloud.importObject() |获取云对象引用以调用云对象接口 [详情](uniCloud/cloud-obj.md) | +|uniCloud.callFunction() |客户端调用云函数 [详情](uniCloud/cf-functions.md?id=clientcallfunction) | +|uniCloud.database() |客户端访问云数据库,获取云数据库对象引用 [详情](uniCloud/clientdb.md) | +|uniCloud.uploadFile() |客户端直接上传文件到云存储 [详情](uniCloud/storage.md?id=uploadfile) | +|uniCloud.getTempFileURL() |客户端获取云存储文件的临时路径 [详情](uniCloud/storage.md?id=gettempfileurl) | +|uniCloud.chooseAndUploadFile() |客户端选择文件并上传 [详情](uniCloud/storage.md?id=chooseanduploadfile) | +|uniCloud.getCurrentUserInfo() |获取当前用户信息 [详情](#client-getcurrentuserinfo) | +|uniCloud.init() |同时使用多个服务空间时初始化额外服务空间 [详情](uniCloud/init.md) | +|uniCloud.addInterceptor() |新增拦截器 [详情](#add-interceptor) | +|uniCloud.removeInterceptor() |移除拦截器 [详情](#remove-interceptor) | +|uniCloud.onResponse() |监听服务端(云函数、云对象、clientDB)响应 [详情](#on-response) | +|uniCloud.offResponse() |移除监听服务端(云函数、云对象、clientDB)响应 [详情](#off-response) | +|uniCloud.onNeedLogin() |监听需要登录事件 [详情](#on-need-login) | +|uniCloud.offNeedLogin() |移除监听需要登录事件 [详情](#off-need-login) | +|uniCloud.onRefreshToken() |监听token更新事件 [详情](#on-refresh-token) | +|uniCloud.offRefreshToken() |移除监听token更新事件 [详情](#off-refresh-token) | ### 获取当前用户信息getCurrentUserInfo@client-getcurrentuserinfo @@ -251,6 +256,113 @@ uniCloud.onResponse(logResponse) uniCloud.offResponse(logResponse) ``` + +### 监听需要登录事件@on-need-login + +> 新增于HBuilderX 3.5.0 + +用于监听客户端需要登录事件,此接口需要搭配uniIDRouter使用,参考:[uniIDRouter](uniCloud/uni-id.md?id=uni-id-router) + +代码示例: + +```js +uniCloud.onNeedLogin(function(event) { + // event格式见下方说明 +}) +``` + +**响应格式** + +```js +interface OnNeedLoginEvent { + errCode: number | string, + errMsg: string, + uniIdRedirectUrl: string // 触发onNeedLogin页面前的页面地址(包含路径和参数的完整页面地址) +} +``` + +**注意** + +- 开发者自定监听onNeedLogin事件后,uniIDRouter的自动跳转登录页面功能会禁用,由开发者在`onNeedLogin`内自行处理跳转 + +### 移除需要登录事件的监听@off-need-login + +> 新增于HBuilderX 3.5.0 + +**注意** + +- 要移除的监听内方法需和添加的方法一致才可以移除,详情见下方示例 + +```js +// 错误用法,无法移除监听 +uniCloud.onNeedLogin(function(e) { + console.log(e) +}) +uniCloud.offNeedLogin(function(e) { + console.log(e) +}) + +// 正确用法 +function log(e) { + console.log(e) +} +uniCloud.onNeedLogin(log) +uniCloud.offNeedLogin(log) +``` + + +### 监听token刷新事件@on-refresh-token + +> 新增于HBuilderX 3.5.0 + +用于监听客户端token刷新事件,包括云对象返回newToken时自动更新token及clientDB自动更新token,注意uni-id-co登录返回的token也会触发此事件 + +代码示例: + +```js +uniCloud.onRefreshToken(function(event) { + // event格式见下方说明 +}) +``` + +**响应格式** + +```js +interface OnRefreshTokenEvent { + token: string, + tokenExpired: number +} +``` + +**注意** + +- 开发者自定监听onNeedLogin事件后,uniIDRouter的自动跳转登录页面功能会禁用,由开发者在`onNeedLogin`内自行处理跳转 + +### 移除需要登录事件的监听@off-need-login + +> 新增于HBuilderX 3.5.0 + +**注意** + +- 要移除的监听内方法需和添加的方法一致才可以移除,详情见下方示例 + +```js +// 错误用法,无法移除监听 +uniCloud.onNeedLogin(function(e) { + console.log(e) +}) +uniCloud.offNeedLogin(function(e) { + console.log(e) +}) + +// 正确用法 +function log(e) { + console.log(e) +} +uniCloud.onNeedLogin(log) +uniCloud.offNeedLogin(log) +``` + ## 属性 ### 获取当前uniCloud实例的服务商 diff --git a/docs/uniCloud/uni-id-common.md b/docs/uniCloud/uni-id-common.md new file mode 100644 index 000000000..5585da50e --- /dev/null +++ b/docs/uniCloud/uni-id-common.md @@ -0,0 +1,158 @@ +uni-id-common是uni-id的公共逻辑部分。不同于旧版本uni-id,uni-id-common仅包含token校验、生成及刷新功能,而仅在用户中心有用的接口对应的实现都挪到了uni-id-co内。这样不仅减小了公共模块的体积,也简化了学习成本。 + +一般开发者无需了解uni-id-common公共模块的API,直接使用[uni-id-pages](uniCloud/uni-id-pages.md) + +如果想了解uni-id-common公共模块内部实现,可以阅读本章节。 + +## uni-id-common公共模块的API列表@api + +### 创建uni-id实例@create-instance + +用法:`uniID.createInstance(Object CreateInstanceParams);` + +CreateInstanceParams内可以传入云函数context,也可以传入clientInfo参数,作用和context类似。方便在云对象内获取clientInfo后直接传入,[什么是云对象?](uniCloud/cloud-obj.md)。 + +```js +// 云函数代码,传入context +const uniID = require('uni-id-common') +exports.main = async function(event,context) { + context.APPID = '__UNI__xxxxxxx' // 替换为当前客户端的APPID,通过客户端callFunction请求的场景可以使用context.APPID获取 + context.PLATFORM = 'h5' // 替换为当前客户端的平台类型,通过客户端callFunction请求的场景可以使用context.PLATFORM获取 + context.LOCALE = 'zh-Hans' // 替换为当前客户端的语言代码,通过客户端callFunction请求的场景可以使用context.LOCALE获取 + const uniIDIns = uniID.createInstance({ // 创建uni-id实例 + context: context, + // config: {} // 完整uni-id配置信息,使用config.json进行配置时无需传此参数 + }) + payload = await uniIDIns.checkToken(event.uniIdToken) // 后续使用uniIDIns调用相关接口 + if (payload.code) { + return payload + } + const res = await uniIDIns.updateUser({ + uid: payload.uid, + nickname: 'user nickname' + }) + return res +} + +// 云对象代码传入clientInfo +const uniID = require('uni-id-common') +module.exports = { + _before() { + const clientInfo = this.getClientInfo() + this.uniID = uniID.createInstance({ // 创建uni-id实例,其上方法同uniID + clientInfo + }) + }, + login() { + // ... + // this.uniID.login() + } +} +``` + +**为什么需要自行创建uni-id实例** + +默认情况下uni-id-common某些接口会自动从全局context内获取客户端的PLATFORM(平台,如:app、h5、mp-weixin)等信息。 + +在单实例多并发的场景下可能无法正确获取(全局对象会被后面的请求覆盖,可能会导致前面一次请求使用了后面一次请求的PLATFORM信息)。因此推荐在开启云函数单实例多并发后,自行为uni-id传入context。 + +此外云函数url化时无法获取客户端信息,也需要使用这种方式将客户端信息传入uni-id。 + +### token校验@checktoken + +一个校验客户端发起请求(uniCloud.callFunction)自带的uniIdToken,获得用户的uid、token、token的过期时间、角色、权限、用户信息(uni-id-users全部字段)的API。 + +这是非常高频且重要的API通常用于换取操作当前云函数的用户Id。 + +#### 思考 + +如果你并没有服务端开发经验,可能会想:为什么需要通过token去换取用户Id,而不是让客户端直接传递用户Id更方便? +这里就涉及到安全问题,有一句话叫做:“前端传递的参数都是不可信任的”。比如:你去银行取款,柜台会要求出示你的身份证来证明你是谁,而不是你直接告诉银行柜台你是谁就管用。否则这是一个极大的安全漏洞。 +综上所述:所有服务端操作涉及账户信息相关内容,都需要使用token来获得,而不是使用前端传递的参数。 + +用法:`uniID.checkToken(String token, Object checkTokenOptions)` + +**参数说明** + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| token | String | 是 |客户端callFunction带上的token | +| options | object | 否 |checkToken方法的选项 | +|  |- autoRefresh| boolean | 否 |是否需要自动判断刷新token,默认true | + +**说明** + +- 角色内包含admin时返回的permission是一个空数组,因此判断一个用户是否有权限时应注意admin角色额外进行判断 + +**响应参数** + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| errCode | Number|String|错误码,0表示成功 | +| message | String |详细信息 | +| uid | String |用户Id,校验成功之后会返回 | +| token | String |用户token快要过期时,新生成的token,只有在config内配置了`tokenExpiresThreshold`的值时才会有此行为 | +| tokenExpired | TimeStamp |新token的过期时间,单位毫秒 | +| role | Array |- | +| permission | Array |用户权限列表。 | + +uni-id使用jwt生成token,jwt所生成的token包含三部分,其中存储的信息为明文信息,uni-id只根据tokenSecret来校验客户端token是否合法。 + + +角色权限将被缓存在token中,此举能减少或消除checkToken的查库次数(有效节省费用、减少响应时间) + +**注意:** + +- 客户端会自动查找storage内的token在callFunction时插入 +- HBuilderX 2.9.5+ 客户端允许开发者自行在callFunction时传入uniIdToken,此时不再从storage获取token +- HBuilderX 2.8.0版本起token存储在storage内推荐使用使用蛇形`uni_id_token`,会在一段时间内兼容驼峰形式`uniIdToken` + +### 主动刷新token@refresh-token + +> 新增于uni-id 3.3.14 + +用法:`uniID.refreshToken(Object RefreshTokenParams);` + +**参数说明** + +| 字段| 类型 | 必填| 说明 | +| --- | --- | --- | --- | +| token | String| 是 |用户token| + +**示例** + +```js +const { + token, + tokenExpired +} = await uniID.refreshToken({ + token: 'xxx' +}) +``` + +**注意** + +- 刷新token时会自动更新token内uid对应的角色权限 + +### 生成token@createtoken + +用法:`uniID.createToken(Object CreateTokenParams)` + +**参数说明** + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| uid | String| 是 |用户Id | +| role | Array | 否 |指定缓存在token内的角色| +| permission| Array | 否 |指定缓存在角色内的权限 | + +**响应参数** + +| 字段 | 类型 | 必填| 说明 | +| --- | --- | --- | --- | +| token | String| 是 |生成的token | +| tokenExpired| Number| 是 |token过期时间对应的时间戳| + +**说明** + +- 创建token时如果未传角色权限会自动获取uid对应的角色权限 diff --git a/docs/uniCloud/uni-id-pages.md b/docs/uniCloud/uni-id-pages.md new file mode 100644 index 000000000..21a31a196 --- /dev/null +++ b/docs/uniCloud/uni-id-pages.md @@ -0,0 +1,1992 @@ +# 概述 + +`uni-id-pages`,是`uni-id`体系的一部分。 + +它基于`uni-id-common`提供了一批现成的、开源的、登录注册账户管理相关的前端页面和云端云对象。 + +它是一个云端一体页面组的[uni_modules](https://uniapp.dcloud.net.cn/plugin/uni_modules.html),含前端页面和后端云对象(uni-id-co)。 + +开发者在项目中引入`uni-id-pages`,账户管理的功能无需自己再开发。由于源码的开放性和层次结构清晰,有二次开发需求也很方便调整。 + +`uni-id-pages`的功能包括: + +- 注册账号: + + 用户名和密码 +- 免密登陆(首次登陆自动注册): + + [app一键登录](https://uniapp.dcloud.io/univerify.html) + + 短信验证码登录 + + 微信登录(自动获取头像和昵称) + + 苹果登录 + + 支付宝小程序登录 +- 密码登陆 + + 用户名/手机号和密码登陆 +- 账户管理 + + 个人中心 + + 头像更换 + + 修改昵称 + + 非手机号登录时的绑定手机号码 + * App端支持:[一键绑定](https://uniapp.dcloud.io/univerify.html) + * 微信小程序端支持:[获取微信绑定的手机号](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html) + * 全端支持:短信验证码校验 + + 找回密码(仅账号有绑定手机号码可用) + + 退出登陆 + + 注销账号(上架国内App应用市场必备) +- 隐私政策 + + +# 目录结构 +
+
+├─uni_modules									存放[uni_module](/uni_modules)规范的插件。
+│	├─其他module
+│	└─uni-id-pages
+│		├─uniCloud
+│		│	├─cloudfunctions 					云函数目录
+│		│	│	└─uni-id-co		 				集成调用uni-id方法的云对象
+│		│	│		├─common 					公用逻辑
+│		│	│		├─config 					配置
+│		│	│		│  └─permission.js 			调用接口所需的权限配置
+│		│	│		├─lang 						国际化目录
+│		│	│		├─lib 						基础功能,不建议修改此目录下文件
+│		│	│		│  ├─third-party			三方平台接口
+│		│	│		│  └─utils					基础功能
+│		│	│		├─middleware				中间件
+│		│	│		└─module					分模块存放的云对象方法
+│		│	└─database
+│		│		├─uni-id-log.schema.json		登录日志表,表结构文件
+│		│		├─uni-id-roles.schema.json		用户角色,表结构文件
+│		│		└─uni-id-users.schema.json		用户表,表结构文件
+│		├─common
+│		│	├─login-page.scss					登录页面公共样式
+│		│	├─login-page.mixin.js				登录页面公共mixin文件
+│		│	└─loginSuccess.js					登录成功后执行的文件
+│		├─components
+│		│	├─cloud-image				        	uniCloud云存储图片解析组件
+│		│	│	└─cloud-image.vue
+│		│	├─uni-id-pages-agreements				同意用户服务协议&隐私政策条款组件
+│		│	│	└─uni-id-pages-agreements.vue
+│		│	├─uni-id-pages-avatar					用户头像组件
+│		│	│	└─uni-id-pages-avatar.vue
+│		│	├─uni-id-pages-bind-mobile				通过微信获取手机号码绑定个人资料
+│		│	│	└─uni-id-pages-bind-mobile.vue
+│		│	├─uni-id-pages-fab-login				悬浮的切换登录方式组件
+│		│	│	└─uni-id-pages-fab-login.vue
+│		│	├─uni-id-pages-sms-form					获取短信验证码组件
+│		│	│	└─uni-id-pages-sms-form.vue
+│		│	└─uni-id-pages-user-profile				获取用户信息组件
+│		│		└─uni-id-pages-user-profile.vue
+│		├─pages
+│		│	├─common
+│		│	│	└─webview				用于实现应用内浏览或打开《用户协议和隐私协议》URL链接页面
+│		│	│		└─webview.vue
+│		│	├─login						
+│		│	│	├─login-smscode.vue		短信验证码登录
+│		│	│	├─login-withoutpwd.vue	免密码登录
+│		│	│	└─login-withpwd.vue		密码登录
+│		│	├─register					
+│		│	│	├─register.vue			注册账号页
+│		│	│	└─validator.js			注册账号页的表单验证规则文件
+│		│	├─retrieve					重置密码
+│		│	│	└─retrieve.vue
+│		│	└─userinfo
+│		│		├─bind-mobile.vue		绑定手机号码
+│		│		├─cropImage.vue			裁剪图片
+│		│		├─deactivate.vue		注销账号
+│		│		└─userinfo.js			注册账号页的表单验证规则文件
+│		├─static					 	静态资源目录
+│		├─changelog.md					更新日志
+│		├─config.js						uni-id-pages的配置文件
+│		├─init.js						初始化文件
+│		├─package.json					包管理文件
+│		└─readme.md						插件自述文件
+
+
+完整的uni-app目录结构[详情](https://uniapp.dcloud.io/frame?id=%e7%9b%ae%e5%bd%95%e7%bb%93%e6%9e%84) + +# 客户端 +## 初始化 +你需要在App.vue中初始化`uni-id-pages`的`init.js`文件 +示例代码如下: +```js + +``` +## 配置@config +路径:`/uni_modules/uni-id-pages/config.js` + +完整示例: +```js +export default { + //调试模式 + "debug":true, + /* + 登录类型 未列举到的或运行环境不支持的,将被自动隐藏。 + 如果需要在不同平台有不同的配置,直接用条件编译即可 + */ + "loginTypes": [ + "univerify", + "weixin", + "username", + "apple", + "smsCode" + ], + //政策协议 + "agreements": { + "serviceUrl": "https://xxx", //用户服务协议链接 + "privacyUrl": "https://xxx", //隐私政策条款链接 + // 哪些场景下显示,1.注册(包括登录并注册,如:微信登录、苹果登录、短信验证码登录)、2.登录(如:用户名密码登录) + "scope": ['register', 'login'] + } +} +``` + +### 字段介绍 +|字段 |类型 |描述 | +|-- |-- |-- | +|debug |Boolean|调试模式 | +|loginTypes |Array |登录方式 | +|agreements |Array |隐私政策 | + +#### 调试模式@debug +开启debug将在应用启动时,自动发起一次网络请求(调用`uni-id-co`的`getSupportedLoginType`)检查:本地uni-id-pages中配置的登录方式,服务器端是否已经配置正确,否则将抛出异常。 + +#### 登录方式@loginTypes +|字段 |描述|平台差异| +|-- |-- |-- | +|univerify |[一键登录](https://uniapp.dcloud.io/univerify.html) |App 3.0.0+| +|smsCode |短信验证码登录 || +|weixin |微信登录 |App,微信小程序 | +|apple |苹果登录[Apple登录](https://ask.dcloud.net.cn/article/36651) | iOS13+支持,App 2.4.7+| +|username |用户名密码登录 || + +##### 配置示例 +```js +export default { + "loginTypes": ["username","smsCode"] +} +``` +如上示例配置,表示启用:账号密码登录、验证码登录。 + +同理配置为: +```js +export default { + "loginTypes": ["weixin","username","smsCode"] +} +``` +则表示启用:微信登录、验证码登录、账号密码登录。 + +###### 平台差异性配置 +如果你希望在不同的平台有不同的登录方式,直接使用[条件编译](https://uniapp.dcloud.io/platform?id=%e6%9d%a1%e4%bb%b6%e7%bc%96%e8%af%91)即可。 +如下配置,即表示仅在APP端启用`短信验证码登录` +```js +export default { + "loginTypes": [ + "username","univerify","weixin","apple" + // #ifdef APP-PLUS + ,"smsCode" + // #endif + ] +} +``` + +**注意:** iOS的App Store应用规则:应用若支持三方社交登录服务(如:一键登录、微信登录等),则必须同时向用户提供“以苹果账号登录”的选项。即:如果你的应用不支持三方登录,那么可以不带上苹果登录,如果你的应用支持三方登录,那必须同时把苹果登录也带上。 + +以上配置仅开启前端登录入口,实现功能还需: +1. 开通对应登录方式服务,获得服务密钥,并在服务端`uni-id`模块的配置文件中完成配置。详情:[登陆服务开通与配置](#登陆服务开通与配置) +2. 如果是APP端,`一键登录`、`微信登录`、`苹果登录`,需要在`manifest.json`中勾选对应模块(微信登录必须配置:微信开发平台申请应用appID的值),并完成打包后才可用(自定义调试基座包和正式包均可)。 + +#### 隐私政策@agreements +|字段 |类型 |描述 | +|-- |-- |-- | +|serviceUrl |String | 用户服务协议网络链接 | +|privacyUrl |String | 隐私政策网络链接 | +|scope |Object | 作用于哪些场景 | + +##### 作用于哪些场景@scope +|字段 |类型 |描述 | +|-- |-- |-- | +|register |String |注册(包括登录并注册,如:微信登录、苹果登录、短信验证码登录) | +|login |String |登录(如:用户名密码登录) | + +一款规范的小程序或要上架到国内应用商店app必须提供《用户协议和隐私协议》,参考模版:[隐私权政策模板.zip](https://ask.dcloud.net.cn/file/download/file_name-6ZqQ56eB5p2D5pS/562W5qih5p2/LnppcA==__url-Ly9pbWctY2RuLXRjLmRjbG91ZC5uZXQuY24vdXBsb2Fkcy9hcnRpY2xlLzIwMjAwMjE0LzUyMDRmMWU2Y2Q3NjcwZWE0YTJjMjBmZGRhMTBhMDdh) + +更多因合规问题[详情](https://uniapp.dcloud.io/tutorial/android-store.html#app%E5%9B%A0%E5%90%88%E8%A7%84%E9%97%AE%E9%A2%98%E6%97%A0%E6%B3%95%E4%B8%8A%E6%9E%B6) + +你还可以用markdown编辑《用户协议和隐私协议》[一键分享](https://ask.dcloud.net.cn/article/37573)上传到[前端网页托管](https://uniapp.dcloud.io/uniCloud/hosting.html#%E7%AE%80%E4%BB%8B)获得链接 + +## 登录页面 +`uni-id-pages`提供了5种登录方式(账号密码登录,一键登录,短信验证码登录,微信登录,苹果登录),2个页面:密码登录和免密登录。 +- 密码登录页面路径: `/uni_modules/uni-id-pages/pages/login/login-withpwd` +- 免密登录页面路径: `/uni_modules/uni-id-pages/pages/login/login-withoutpwd` + +执行`uni.navigateTo`打开登录页面,会默认使用配置中`loginTypes`值的第一项为登录方式。 +例如`loginTypes`:`["weixin","apple","univerify"]`会以`weixin`,即`微信登录`为默认登录方式 + +如果你希望:指定登录方式`uni-id-page`支持通过传递参数`type`,指定登录方式。例如:指定苹果登录,使用如下代码即可 +```js +uni.navigateTo({ + "url":"/uni_modules/uni-id-pages/pages/login/login-withoutpwd?type=apple" +}) +``` + +可以配套使用[uniIdRouter](uniCloud/uni-id.md?id=uni-id-router);当用户未登录,但访问了需强制登录的页面,或接口提示token无效或过期(响应体以TOKEN_INVALID开头)时均需要打开登录页面。你需要把以上两个路径路径定义为`loginPage`。 + +# 登陆服务开通与配置 +服务端`uni-id`的密钥信息统一在`uni-config-center`中配置,路径:`uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json` +以下简称:`uni-id配置文件`,完整的配置信息[详情](https://uniapp.dcloud.io/uniCloud/uni-id.html#config) + +## 一键登录 +一键登录是运营商提供的、比短信验证码更方便、更安全、更便宜的方案。[详见](https://uniapp.dcloud.net.cn/univerify)。 +- 使用本功能需要在[DCloud开发者中心 -> ](https://dev.dcloud.net.cn/uniLogin)开通并充值 +- 模块配置:`manifest.json`-->`App模块配置` -->`OAuth(登录鉴权)`-->` 一键登录`,点击后面的`开通配置`,在随后打开的web界面中,同意协议,并点击充值按钮充值。如只是测试,可以只充值1元钱。如果你已经确定包名,则可以在web界面点击“添加应用”,提交审核。这个是正式打包必须的。真机运行可以跳过此环节。记住页面上展示的`apiKey`和`apiSecret`,下一步需要用到。 +- uni-id配置:`uni-id配置文件` --> `service` --> `univerify`,填写`appid`、`apiKey`和 `apiSecret`。`appid`就是`manifest`里的`appid`。`apiKey`和`apiSecret`则是从上一步的web界面得来的。 + +## 微信登录 +- APP端申请微信登录需要在**微信开放平台**申请移动应用,获得的appid和appsecret,用于微信登录。[微信开放平台](https://open.weixin.qq.com/) +- 微信 appid 申请步骤:[https://ask.dcloud.net.cn/article/208](https://ask.dcloud.net.cn/article/208)。 +- iOS平台微信SDK配置通用链接:[https://uniapp.dcloud.io/api/plugins/universal-links.html](https://uniapp.dcloud.io/api/plugins/universal-links.html)。 +- 模块配置:`manifest.json` --> `App模块配置` --> `OAuth(登录鉴权)` --> `勾选微信登录` --> 填写`appid`、`appsecret`、`ios平台通用链接`。 +- uni-id配置:`uni-id配置文件` --> `app-plus` --> `oauth` --> `weixin`,填写`appid`、`appsecret` + +## 苹果登录集成指南 +- 模块配置:`manifest.json` --> `App模块配置` --> OAuth(登录鉴权)勾选`苹果登录`,[IOS苹果授权登录参考文档](https://ask.dcloud.net.cn/article/36651)。如不发布到Appstore,不需要配置此项 +- uni-id配置:`uni-id配置文件` --> `app-plus` --> `oauth` --> `apple` 填写`bundleId`。 +- 关联域配置:`manifest.json` --> `App常用其他设置` --> `iOS设置` --> `关联域(Associated Domains)` 填写配置 [参考教程](https://ask.dcloud.net.cn/article/36393)。如不发布到Appstore,不需要配置此项 + +## 短信验证码 +为了方便开发调试,`uni-id-pages`未配置短信登陆时,自动启动测试模式;直接使用:123456作为短信验证码即可。 +- 使用本功能需要在[DCloud开发者中心 -> 短信验证码](https://dev.dcloud.net.cn/uniSms)开通并充值 +- 教程参考[短信服务开通指南](https://ask.dcloud.net.cn/article/37534) +- 密钥配置:`uni-id配置文件` --> `service` --> `sms` 填写相关密钥信息。 + + +# uni-id-co +## uni-id-co的API列表@co-api + +前端调用云对象`uni-id-co`内的方法前应先获取云对象的引用,代码如下 + +```js +const uniIDCo = uniCloud.importObject('uni-id-co') +``` + +### 目录说明 + +```text +├─common // 公用逻辑 +├─config // 配置 +│ └─permission.js // 调用接口所需的权限配置 +├─lang // 国际化目录 +├─lib // 基础功能,不建议修改此目录下文件 +│ ├─third-party // 三方平台接口 +│ └─utils // 基础功能 +├─middleware // 中间件 +└─module // 分模块存放的云对象方法 +``` + +### 公共响应参数@co-public-response + +`uni-id-co`所有api返回值均满足[uniCloud响应体规范](uniCloud/cf-functions?id=resformat) + +返回值示例 + +```js +{ + errCode: 0, // 错误码,详见错误码列表 + errMsg: '', // 错误信息,uni-id-co会自动根据客户端语言对错误信息进行国际化 + newToken: { // 注册、登录、刷新token等接口会自动返回新token,uniCloud客户端sdk会自动将新token及过期时间存储在storage内(需要HBuilderX 3.4.13及以上版本) + token, + tokenExpired + }, + // ...其余参数 +} +``` + +**注意** + +- 需要校验token的接口在token即将过期时也会返回newToken,token即将过期的阈值由开发者自行配置 + +### 注册登录和登出@register-login-logout + +#### 注册超级管理员@register-admin + +**接口形式** + +```js +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过期时间 | + +**注意** + +- 系统中仅可存在一个超级管理员 + +#### 用户名密码注册用户@register-user + +**接口形式** + +```js +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过期时间 | + +#### 密码登录@login + +**接口形式** + +```js +await uniIDCo.login({ + username, + password, + captcha +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|username |string |和mobile、email三选一 |用户名 | +|mobile |string |和username、email三选一 |手机号 | +|email |string |和username、mobile三选一 |邮箱 | +|password |string |是 |密码 | +|captcha |string |否 |图形验证码 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +#### 短信验证码登录@login-by-sms + +手机号已存在时登录,否则注册 + +**接口形式** + +```js +await uniIDCo.loginBySms({ + mobile, + code, + captcha, + inviteCode +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|mobile |string |是 |手机号 | +|code |string |是 |短信验证码 | +|captcha |string |否 |图形验证码 | +|inviteCode |string |否 |邀请码,仅注册时生效 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +#### 一键登录@login-by-univerify + +手机号已存在时登录,否则注册 + +**接口形式** + +```js +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过期时间 | + +#### 微信登录@login-by-weixin + +微信账号已存在时登录,否则注册 + +**接口形式** + +```js +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过期时间 | + +#### QQ登录@login-by-qq + +QQ账号已存在时登录,否则注册 + +**接口形式** + +```js +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过期时间 | + +#### 支付宝登录@login-by-alipay + +支付宝账号已存在时登录,否则注册 + +**接口形式** + +```js +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过期时间 | + +#### 苹果登录@login-by-apple + +苹果账号已存在时登录,否则注册 + +**接口形式** + +```js +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 + +**接口形式** + +```js +await uniIDCo.logout() +``` + +**参数说明** + +无 + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | + +### 绑定账号@bind + +#### 使用短信验证码绑定手机号@bind-mobile-by-sms + +**接口形式** + +```js +await uniIDCo.bindMobileBySms({ + mobile, + code, + captcha +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|mobile |string |是 |手机号码 | +|code |string |是 |短信验证码 | +|captcha|string |否 |图形验证码 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +**注意** + +- 仅在用户token即将过期时返回新newToken + +#### 使用一键登录绑定手机号@bind-mobile-by-univerify + +**接口形式** + +```js +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 + +#### 通过微信绑定手机号@bind-mobile-by-mp-weixin + +使用此接口时务必注意,微信小程序的规则是客户端应先使用checkSession接口检测上次获取的sessionKey是否仍有效。如果有效则直接使用上次存储的sessionKey即可,如果无效应重新调用login接口再次刷新sessionKey。微信小程序登录,绑定小程序微信账号时会自动更新用户表的sessionKey。 + +**接口形式** + +```js +await uniIDCo.bindMobileByMpWeixin({ + encryptedData, + iv +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|encryptedData |string |是 |微信小程序获取手机号返回的encryptedData参数| +|iv |string |是 |微信小程序获取手机号返回的iv参数 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +**注意** + +- 仅在用户token即将过期时返回新newToken + +#### 绑定微信@bind-weixin + +**接口形式** + +```js +await uniIDCo.bindWeixin({ + code +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|code |string |是 |微信登录返回的code参数 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +**注意** + +- 仅在用户token即将过期时返回新newToken + +#### 绑定QQ@bind-qq + +**接口形式** + +```js +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 + +#### 绑定支付宝账号@bind-alipay + +**接口形式** + +```js +await uniIDCo.bindAlipay({ + code +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|code |string |是 |支付宝小程序登录返回的code参数 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +**注意** + +- 仅在用户token即将过期时返回新newToken + +#### 绑定苹果账号@bind-apple + +**接口形式** + +```js +await uniIDCo.bindApple({ + identityToken +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|identityToken |string |是 |苹果登录返回的identityToken参数| + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +**注意** + +- 仅在用户token即将过期时返回新newToken + +### 用户信息@user-info + +#### 修改密码@update-pwd + +**接口形式** + +```js +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 + +#### 通过短信验证码重置密码@reset-pwd-by-sms + +**接口形式** + +```js +await uniIDCo.resetPwdBySms({ + mobile, + code, + password, + captcha +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|mobile |string |是 |手机号 | +|code |string |是 |短信验证码 | +|password |string |是 |密码 | +|captcha |string |否 |图形验证码 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | + + +#### 接受邀请@accept-invite + +**接口形式** + +```js +await uniIDCo.acceptInvite({ + inviteCode +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|inviteCode |string |是 |邀请码 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +**注意** + +- 仅在用户token即将过期时返回新newToken + +#### 获取邀请的用户@get-invited-user + +**接口形式** + +```js +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 + + +### 安全验证@verifier + +#### 创建图形验证码@create-captcha + +**接口形式** + +```js +await uniIDCo.createCaptcha({ + scene +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|scene |string |是 |图形验证码使用场景,参考:[图形验证码场景](#captcha-scene) | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | + +#### 刷新图形验证码@refresh-captcha + +**接口形式** + +```js +await uniIDCo.refreshCaptcha({ + scene +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|scene |string |是 |图形验证码使用场景,参考:[图形验证码场景](#captcha-scene) | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | + +#### 发送短信@send-sms-code + +**接口形式** + +```js +await uniIDCo.sendSmsCode({ + mobile, + captcha, + scene +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|mobile |string |是 |手机号码 | +|captcha|string |是 |图形验证码 | +|scene |string |是 |短信类型,参考:[短信类型](#sms-scene) | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | + +### 其他用户端接口@utils + +#### 刷新token@refresh-token + +**接口形式** + +```js +await uniIDCo.refreshToken() +``` + +**参数说明** + +无 + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + + +#### 更新/设置uni-push clientId@set-push-cid + +如未使用`uni-push 2.0`无需关注此接口。此接口用于更新uni-id-device表的unipush_clientid字段,用于按客户端、用户等维度推送消息 + +**接口形式** + +```js +await uniIDCo.setPushCid({ + pushClientId +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|pushClientId |string |是 |客户端获取的uni-push对应的clientId | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|newToken |object |token信息 | +| |- token |string |token | +| |- tokenExpired|string |token过期时间 | + +### 管理接口@admin + +#### 管理员新增用户@add-user + +**接口形式** + +```js +await uniIDCo.addUser({ + username, + password, + authorizedApp, + nickname, + role +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|username |string |是 |用户名 | +|password |string |是 |密码 | +|authorizedApp |Array<string>|否 |允许登录的app列表 | +|nickname |string |否 |昵称 | +|role |Array<string>|否 |用户角色 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | + + +#### 授权用户登录指定客户端@authorize-app-login + +**接口形式** + +```js +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 + +#### 移除用户登录授权@remove-authorized-app + +**接口形式** + +```js +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 + +#### 设置允许登录的应用列表@set-authorized-app + +**接口形式** + +```js +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 + +### 开发接口@dev + +#### 获取支持的登录方式@get-supported-login-type + +**接口形式** + +```js +await uniIDCo.getSupportedLoginType({ + appId, + platform +}) +``` + +**参数说明** + +|参数名 |类型 |必填 |说明 | +|-- |-- |-- |-- | +|appId |string |否 |要查询登录方式的应用DCloud AppId,不传时为当前应用 | +|platform |string |否 |要查询登录方式的应用平台,不传时为当前平台 | + +**返回值** + +|参数名 |类型 |说明 | +|-- |-- |-- | +|errCode |string|number |错误码 | +|errMsg |string |错误信息 | +|supportedLoginType |array |支持的登录方式列表,见下方说明 | + +**supportedLoginType** + +|登录方式 |说明 | +|--- |--- | +|username-password |用户名密码登录 | +|mobile-password |手机号密码登录 | +|email-password |邮箱密码登录 | +|mobile-code |手机号验证码登录 | +|univerify |App一键登录 | +|weixin |微信登录 | +|qq |QQ登录 | +|apple |苹果登录 | +|alipay |支付宝登录 | + +## uni-id-common公共模块的API列表@api + +自`uni-id 4.0.0`起uni-id公共模块内的大部分接口实现移至uni-id-co内,公共模块内仅保留token相关接口 + +一般开发者无需了解uni-id-common公共模块的API,直接使用[uni-id-pages]()即可(**需要补充链接**)。 + +如果想了解uni-id-common公共模块内部实现,可以阅读本章节。 + +### 基础功能@base + +#### 创建uni-id实例@create-instance + +用法:`uniID.createInstance(Object CreateInstanceParams);` + +CreateInstanceParams内可以传入云函数context,也可以传入clientInfo参数,作用和context类似。方便在云对象内获取clientInfo后直接传入,[什么是云对象?](uniCloud/cloud-obj.md)。 + +```js +// 云函数代码,传入context +const uniID = require('uni-id-common') +exports.main = async function(event,context) { + context.APPID = '__UNI__xxxxxxx' // 替换为当前客户端的APPID,通过客户端callFunction请求的场景可以使用context.APPID获取 + context.PLATFORM = 'h5' // 替换为当前客户端的平台类型,通过客户端callFunction请求的场景可以使用context.PLATFORM获取 + context.LOCALE = 'zh-Hans' // 替换为当前客户端的语言代码,通过客户端callFunction请求的场景可以使用context.LOCALE获取 + const uniIDIns = uniID.createInstance({ // 创建uni-id实例 + context: context, + // config: {} // 完整uni-id配置信息,使用config.json进行配置时无需传此参数 + }) + payload = await uniIDIns.checkToken(event.uniIdToken) // 后续使用uniIDIns调用相关接口 + if (payload.code) { + return payload + } + const res = await uniIDIns.updateUser({ + uid: payload.uid, + nickname: 'user nickname' + }) + return res +} + +// 云对象代码传入clientInfo +const uniID = require('uni-id-common') +module.exports = { + _before() { + const clientInfo = this.getClientInfo() + this.uniID = uniID.createInstance({ // 创建uni-id实例,其上方法同uniID + clientInfo + }) + }, + login() { + // ... + // this.uniID.login() + } +} +``` + +**为什么需要自行创建uni-id实例** + +默认情况下uni-id-common某些接口会自动从全局context内获取客户端的PLATFORM(平台,如:app、h5、mp-weixin)等信息。 + +在单实例多并发的场景下可能无法正确获取(全局对象会被后面的请求覆盖,可能会导致前面一次请求使用了后面一次请求的PLATFORM信息)。因此推荐在开启云函数单实例多并发后,自行为uni-id传入context。 + +此外云函数url化时无法获取客户端信息,也需要使用这种方式将客户端信息传入uni-id。 + +#### token校验@checktoken + +一个校验客户端发起请求(uniCloud.callFunction)自带的uniIdToken,获得用户的uid、token、token的过期时间、角色、权限、用户信息(uni-id-users全部字段)的API。 + +这是非常高频且重要的API通常用于换取操作当前云函数的用户Id。 + +##### 思考 + +如果你并没有服务端开发经验,可能会想:为什么需要通过token去换取用户Id,而不是让客户端直接传递用户Id更方便? +这里就涉及到安全问题,有一句话叫做:“前端传递的参数都是不可信任的”。比如:你去银行取款,柜台会要求出示你的身份证来证明你是谁,而不是你直接告诉银行柜台你是谁就管用。否则这是一个极大的安全漏洞。 +综上所述:所有服务端操作涉及账户信息相关内容,都需要使用token来获得,而不是使用前端传递的参数。 + +用法:`uniID.checkToken(String token, Object checkTokenOptions)` + +**参数说明** + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| token | String | 是 |客户端callFunction带上的token | +| options | object | 否 |checkToken方法的选项 | +|  |- autoRefresh| boolean | 否 |是否需要自动判断刷新token,默认true | + +**说明** + +- 角色内包含admin时返回的permission是一个空数组,因此判断一个用户是否有权限时应注意admin角色额外进行判断 + +请务必阅读一下此文档:[关于缓存角色权限的说明](uniCloud/uni-id.md?id=cache-permission-in-token) + +**响应参数** + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| errCode | Number|String|错误码,0表示成功 | +| message | String |详细信息 | +| uid | String |用户Id,校验成功之后会返回 | +| token | String |用户token快要过期时,新生成的token,只有在config内配置了`tokenExpiresThreshold`的值时才会有此行为 | +| tokenExpired | TimeStamp |新token的过期时间,单位毫秒 | +| role | Array |- | +| permission | Array |用户权限列表。 | + +uni-id使用jwt生成token,jwt所生成的token包含三部分,其中存储的信息为明文信息,uni-id只根据tokenSecret来校验客户端token是否合法。 + + +角色权限将被缓存在token中,此举能减少或消除checkToken的查库次数(有效节省费用、减少响应时间) + +**注意:** + +- 客户端会自动查找storage内的token在callFunction时插入 +- HBuilderX 2.9.5+ 客户端允许开发者自行在callFunction时传入uniIdToken,此时不再从storage获取token +- HBuilderX 2.8.0版本起token存储在storage内推荐使用使用蛇形`uni_id_token`,会在一段时间内兼容驼峰形式`uniIdToken` + +#### 主动刷新token@refresh-token + +> 新增于uni-id 3.3.14 + +用法:`uniID.refreshToken(Object RefreshTokenParams);` + +**参数说明** + +| 字段| 类型 | 必填| 说明 | +| --- | --- | --- | --- | +| token | String| 是 |用户token| + +**示例** + +```js +const { + token, + tokenExpired +} = await uniID.refreshToken({ + token: 'xxx' +}) +``` + +**注意** + +- 刷新token时会自动更新token内uid对应的角色权限 + +#### 生成token@createtoken + +用法:`uniID.createToken(Object CreateTokenParams)` + +**参数说明** + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| uid | String| 是 |用户Id | +| role | Array | 否 |指定缓存在token内的角色| +| permission| Array | 否 |指定缓存在角色内的权限 | + +**响应参数** + +| 字段 | 类型 | 必填| 说明 | +| --- | --- | --- | --- | +| token | String| 是 |生成的token | +| tokenExpired| Number| 是 |token过期时间对应的时间戳| + +**说明** + +- 创建token时如果未传角色权限会自动获取uid对应的角色权限 + +## uniIdRouter自动路由@uni-id-router + +> 新增于 HBuilderX 3.5.0 + +开发者可以在项目的`pages.json`内配置需要登录的页面,登录页面路径等信息,uniCloud会自动在需要登录且客户端登录状态过期或未登录时跳转到登录页面。 + +结合以下代码及注释了解如何使用`uniIDRouter` + +```json +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "uni-app" + }, + "needLogin": false // 当前页面是否需要登录才可以访问,此配置优先级高于uniIDRouter下的needLogin + }, { + "path": "pages/list/list", + "style": { + "navigationBarTitleText": "uni-app" + }, + "needLogin": false + }, { + "path": "pages/detail/detail", + "style": { + "navigationBarTitleText": "uni-app" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "uni-app", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8" + }, + "uniIDRouter": { + "loginPage": "pages/index/index", // 登录页面路径 + "needLogin": [ + "pages/detail/.*" // 需要登录才可访问的页面列表,可以使用正则语法 + ], + "resToLogin": true // 自动解析云对象及clientDB的错误码,如果是客户端token不正确或token过期则自动跳转配置的登录页面,配置为false则关闭此行为,默认true + } +} + +``` + +与此功能对应的有两个uniCloud客户端api,`uniCloud.onNeedLogin()`和`uniCloud.offNeedLogin()`,开发者在监听onNeedLogin事件后,框架就不再自动跳转到登录页面,而是由开发者在onNeedLogin事件内自行处理。详情参考:[uniCloud.onNeedLogin](uniCloud/client-sdk.md?id=on-need-login) + +**注意** + +- pages.json内有`uniIDRouter`节点上述逻辑才会生效,自HBuilderX 3.5.0起创建空项目模板会自动配置空的`uniIDRouter`节点 + +## 错误码@errcode + +|错误码 |错误信息 |说明 | +|---- |---- |---- | +|0(数字) |成功 |- | +|uni-id-token-expired |登陆状态失效,token已过期 |- | +|uni-id-check-token-failed |token校验未通过 |- | +|uni-id-account-exists |账户已存在 |- | +|uni-id-account-not-exists |账户不存在 |- | +|uni-id-account-conflict |用户账号冲突 |可能会由开发者手动更新数据库导致,正常情况下不应出现 | +|uni-id-account-banned |此账号已封禁 |- | +|uni-id-account-auditing |此账号正在审核中 |- | +|uni-id-account-audit-failed |此账号审核失败 |- | +|uni-id-account-closed |此账号已注销 |- | +|uni-id-captcha-required |请输入图形验证码 |- | +|uni-id-password-error |用户名或密码错误 |- | +|uni-id-invalid-username |用户名不合法 |- | +|uni-id-invalid-password |密码不合法 |- | +|uni-id-invalid-mobile |手机号码不合法 |- | +|uni-id-invalid-email |邮箱不合法 |- | +|uni-id-invalid-nickname |昵称不合法 |- | +|uni-id-invalid-param |参数错误 |- | +|uni-id-param-required |缺少参数 |- | +|uni-id-get-third-party-account-failed |获取第三方账号失败 |- | +|uni-id-get-third-party-user-info-failed|获取第三方用户信息失败 |- | +|uni-id-mobile-verify-code-error |手机验证码错误或已过期 |- | +|uni-id-email-verify-code-error |邮箱验证码错误或已过期 |- | +|uni-id-admin-exists |超级管理员已存在 |- | +|uni-id-permission-error |权限错误 |- | +|uni-id-system-error |系统错误 |- | +|uni-id-set-invite-code-failed |设置邀请码失败 |- | +|uni-id-invalid-invite-code |邀请码不可用 |- | +|uni-id-change-inviter-forbidden |禁止修改邀请人 |- | +|uni-id-bind-conflict |此账号(微信、QQ、手机号等)已被绑定 |- | + +## 多个应用复用相同uni-id-user表 + +有些系统由多个子应用组成,且没有各自独立服务空间,而是需要共享一个服务空间。此时就涉及一个问题,多个应用注册的账户都在uni-id-user表中,如何有效隔离。 + +比如一个打车软件,有乘客端、司机端、管理端,都要注册账户。它们也都有自己的DCloud appID(manifest.json里第一个配置) + +uni-id-user表中有一个数组型字段`dcloud_appid`,可以存贮这个用户有权登陆哪个应用。 + +比如乘客端的appid是`__uni_111111`,司机端appid是`__uni_222222`,那么2个appid都存入`dcloud_appid`,即表示这个用户有权登录这2个应用。 + +### 隔离不同应用的用户@isolate-user + +uni-id 3.3.0版本起用户注册时会自动在用户表的记录内标记为注册应用对应的用户,如果没有单独授权登录其他应用的话则只能登录这个应用。即在乘客端应用注册的,默认只能在乘客端应用登录。 + +如何授权登录其他应用请参考:[授权、禁止用户在特定客户端应用登录](uniCloud/uni-id?id=authorize-app) + +需要注意的是客户端APPID信息是由端上传上来的,并非完全可信,尽量在入口处进行校验。例: + +```js +exports.main = async function(event, context){ + if(context.APPID !== '__UNI__xxx1') { + throw new Error('应用ID非法') + } +} +``` + +**DCloud Appid是一个很重要的配置,如无必要请勿随意更换。** + +> 不同端用户数据通过用户表的dcloud_appid字段隔离,同一个手机号、微信号也可以同时注册管理端和用户端,绑定账号同理。 + +**注意** + +- uni-id会自动在用户表每条用户记录插入`dcloud_appid`字段(此字段是一个数组,标识此用户可以在哪些端登录)。 +- 为兼容旧版本产生的旧数据,针对没有dcloud_appid字段的用户,允许登录任意端。 +- 如果用户数据库记录中`dcloud_appid`字段是一个空数组,表示当前用户不能在任何客户端登录 +- 已有dcloud_appid的用户,如果使用相同的用户标识(用户名、邮箱、手机、微信等)+ 不同的DCloud Appid登录会被判定为不同的用户,如果此时数据库没有对应的记录,会报用户不存在的错误 + +### 隔离不同应用的配置@isolate-config + +> `uni-id 3.3.0`及以上版本 + +uni-id的config.json支持配置为数组,每项都是一个完整的配置,对不同的配置使用`dcloudAppid`字段进行区分(**此字段与项目内的manifest.json里面的DCloud AppId一致**),uni-id会自动根据客户端的appid来判断该使用哪套配置。如果使用云函数url化请参考:[云函数Url化时使用](uniCloud/uni-id?id=url) + +需要注意的是客户端APPID信息是由端上传上来的,并非完全可信,尽量在入口处进行校验。例: + +```js +exports.main = async function(event, context){ + if(context.APPID !== '__UNI__xxx1') { + throw new Error('应用ID非法') + } +} +``` + +**示例** + +> 数组每一项都是一个完整的配置文件,全部选项请参考:[uni-id 配置](uniCloud/uni-id?id=config) + +**注意:如果允许同一账号在不同端使用相同的账号+密码登录需要将不同端的passwordSecret设置成一样的** + +```js +[{ + "dcloudAppid": "__UNI__xxxx1", // 务必替换为对应项目manifest.json内的DCloud Appid + "isDefaultConfig": true, // 默认配置标记,未匹配到dcloudAppid的情况下使用默认配置 + "passwordSecret": "passwordSecret-demo", + "tokenSecret": "tokenSecret-demo", + "tokenExpiresIn": 7200, + "tokenExpiresThreshold": 600 +}, { + "dcloudAppid": "__UNI__xxxx2", // 务必替换为对应项目manifest.json内的DCloud Appid + "passwordSecret": "passwordSecret-demo", + "tokenSecret": "tokenSecret-demo", + "tokenExpiresIn": 7200, + "tokenExpiresThreshold": 600 +}] +``` + + +## 其他功能 + +### 裂变@fission + +在`config.json`内配置了`autoSetInviteCode: true`则在用户注册时会自动给设置不重复的6位邀请码 + +在`config.json`内配置了`forceInviteCode: true`则只有使用邀请码才可以注册。 + +`uni-id-co`在会产生注册行为的接口均添加了inviteCode参数,用于传递邀请码使注册用户接受邀请 + +### 修改passwordSecret@modifysecret + +**注意:通常情况下设定好passwordSecret之后不需要再进行修改,使用此功能时请务必小心谨慎** + +**说明** + +在config.json内修改passwordSecret会导致历史用户无法通过密码登录。但是某些情况下有些应用有修改passwordSecret的需求,例如刚开始使用uni-id时没有自定义passwordSecret,后续需要修改,此时可以使用uni-id 2.0.1版本新增的修改passwordSecret功能。(注意:2.0.1版本验证码表名调整为了`opendb-verify-codes`) + +**如何使用** + +下面以将passwordSecret从`passwordSecret-demo`修改为`qwertyasdfgh`为例介绍如何使用 + +```json +// 旧config.json +{ + "passwordSecret": "passwordSecret-demo" +} + +// 新config.json +{ + "passwordSecret": [{ + "version": 1, + "value": "passwordSecret-demo" + },{ + "version": 2, + "value": "qwertyasdfgh" + }] +} + +``` + +如果在上面基础上再修改passwordSecret为`1q2w3e4r5t`,config.json调整如下 + +> !!!注意只有在数据库内完全没有使用某个版本(`password_secret_version`字段表示了用户密钥版本)密钥的用户才可以将此密钥从config.json内去除。没有`password_secret_version`的用户使用的是最旧版本的passwordSecret,如果存在这样的用户对应的passwordSecret也不可去除。 + +```json +// 新config.json, +{ + "passwordSecret": [{ + "version": 1, + "value": "passwordSecret-demo" + },{ + "version": 2, + "value": "qwertyasdfgh" + },{ + "version": 3, + "value": "1q2w3e4r5t" + }] +} +``` + +**原理** + +uni-id-users表内存储的password字段为使用hmac-sha1生成的hash值,此值不可逆向推出用户真实密码。所以直接修改passwordSecret会导致老用户无法使用密码登录。 + +上述修改通过密钥版本号区分新旧密钥,用户登录时如果密钥版本小于当前最新版本,会为用户更新数据库内存储的password字段,并记录当前使用的密钥版本。 + +用户对应的数据库记录内没有密钥版本的话会使用最低版本密钥进行密码校验,校验通过后为用户更新为最新版密钥对应的password并记录版本号。 + +由于是不可逆加密,理论上passwordSecret泄露不会造成用户的真实密码被泄露,自定义passwordSecret只是进一步加强安全性。 + +### 缓存角色权限@cache-permission-in-token + +使用`uni-id-common`时,token内会缓存用户的角色权限。 + +为什么要缓存角色权限?要知道云数据库是按照读写次数来收取费用的,并且读写数据库会拖慢接口响应速度。 + +详细checkToken流程如下: + +![](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/ed45d350-5a4d-11eb-b997-9918a5dda011.jpg) + +可以看出,旧版token(removePermissionAndRoleFromToken为true时生成的)在checkToken时如需返回权限需要进行两次数据库查询。新版token不需要查库即可返回权限信息。 + +**注意** + +- 由于角色权限缓存在token内,可能会存在权限已经更新但是用户token未过期之前依然是旧版角色权限的情况。可以调短一些token过期时间来减少这种情况的影响。 +- admin角色token内不包含permission,如需自行判断用户是否有某个权限,要注意admin角色需要额外判断一下,写法如下 + ```js + const { + role, + permission + } = await uniID.checkToken(event.uniIdToken) + if(role.includes('admin') || permission.includes('your permission id')) { + // 当前角色拥有'your permission id'对应的权限 + } + ``` + +### 自定义token内容@custom-token + +> uni-id 3.0.7及以上版本,且需要使用[uni-config-center](https://ext.dcloud.net.cn/plugin?id=4425) + +自`uni-id 3.0.0`起,支持在token内缓存用户的角色权限。但是某些情况下开发者可能还希望缓存一些别的东西,以便在客户端能方便的访问(**注意:不可缓存机密信息到token内**)。 + +**用法** + +在`uni-config-center`模块内的uni-id插件内创建`custom-token.js`内容如下: + +```js +module.exports = async (tokenObj) => { + // tokenObj为原始token信息结构如下 + // { + // uid: 'abc', // 用户id + // role: [], // 用户角色列表 + // permission: [] // 用户权限列表,admin角色的用户权限列表为空数组 + // } + + tokenObj.customField = 'hello custom token' // 自定义token字段 + return tokenObj // 注意务必返回修改后的token对象 +} +``` + +uni-id会自动加载custom-token.js进行处理,在所有生成token的操作(包括:登录、注册、token过期自动刷新、开发者自行调用createToken)执行时自动获取新token信息,并生成token。 + +**注意** + +- 使用custom-token时自行调用createToken接口会变为异步操作,需使用`await uniID.createToken(...)` +- 不要删除原始token内的字段 + +### 自定义国际化语言@custom-i8n + +> 新增于uni-id 3.3.10,此功能依赖于[uni-config-center] + +完整词句列表参考:[uni-id中文语言包](https://gitee.com/dcloud/uni-id/blob/master/src/lang/zh-Hans.js) + +**用法** + +在`cloudfunctions/common/uni-config-center/uni-id/lang/`目录下创建`index.js`,内容示例如下: + +```js +module.exports = { + 'zh-hant': { // 语言代码 + 'alipay': '支付寶' + } +} +``` + +uni-id会自动进行语言匹配,无需额外配置 + + +### 云函数Url化时使用@url + +云函数url化时uni-id无法自行获取客户端相关信息,需要开发者自行创建uniID实例并传入相关信息,以下为一个简单示例 + +**注意:实际业务中务必验证一下前端传来的数据的合法性,APPID、PLATFORM等均来自前端** + +```js +// 客户端代码示例 +uni.request({ + url: 'https://xxx.xxx/xxx?appid=your_appid&platform=your_platform&deviceId=your_deviceId' +}) + +// 云函数代码示例 +const uniID = require('uni-id') +exports.main = async function(event, context) { + const { + appid, + platform + } = event.queryStringParameters // 不同类型的请求获取参数的方式略有差异,具体如何取参数请参考:https://uniapp.dcloud.net.cn/uniCloud/http + context.APPID = appid + context.PLATFORM = platform + const uniIDIns = uniID.createInstance({ + context + }) + // uniIDIns.checkToken() 使用uniIDIns来调用uni-id相关接口 +} +``` + + +## uni-id公共模块迁移指南@migration + +### 自1.x.x版本升级到2.x.x@m1to2 + +自2.0.0版本起uni-id调整了验证码表名(这个调整导致了与旧版不兼容),如果要使用2.0.0以上版本需要在数据库中创建opendb-verify-code表(建议直接选择opendb内uni-id下的opendb-verify-code表,会自动创建索引以及表结构) + +### 自2.x.x版本升级到3.x.x@m2to3 + +3.0.0版本起uni-id默认将缓存用户角色权限到token内,关于缓存角色权限的说明请参考:[缓存角色权限](uniCloud/uni-id?id=cache-permission-in-token)。从2.x.x版本升级到3.x.x版本需要根据自己需求分别处理。 + +- 如果不希望缓存角色权限到token内,需要在config.json内配置`"removePermissionAndRoleFromToken": true`。 +- 如果希望升级为缓存角色权限到token内的方案,可以按照以下步骤迁移 + + 各登录接口的needPermission参数不再生效,checkToken校验新token时总是返回角色权限 + + 所有注册用户行为均支持传入角色(role)字段,指定创建用户的角色(需要使用3.0.2及以上版本,此前只有uniID.register接口支持)。由于需要初始生成的token内带有角色权限,所以推荐在注册时就给用户设置好角色。 + +##### uniCloud admin升级uni-id@m2to3-uni-admin + +uniCloud admin可以平滑升级到uni-id 3.0.0。如果要缓存角色权限到token内(uni-id 3.0.0的默认行为),那还有几点可以优化。详细调整如下 + +1. `uniCloud-aliyun\cloudfunctions\uni-admin\middleware\auth.js` + + auth中间件内可以调整为checkToken时不再获取用户信息,这样auth中间件就无需进行数据库查询,可以加速接口响应 + +2. `uniCloud-aliyun\cloudfunctions\uni-admin\controller\app.js` + + 受第一步影响app/init内无法获取用户信息,可以额外调用uniID的getUserInfo获取 + +可以参考此次提交进行调整:[uniCloud admin](https://github.com/dcloudio/uniCloud-admin/commit/8359d699aacb8f7d074fce9aa82a36474cb6e7df) + +##### 使用uni-config-center@uni-config-center + +> uni-id 3.0.7及以上版本 + +从插件市场导入支持uni_modules的uni-id,会自动安装依赖的uni-config-center到uni_modules内。如果此前并没有使用uni-config-center可以直接将uni-id的config.json移至`uni-config-center/uni-id/config.json`即可(可以参照插件市场的uni-id示例项目) + +- uni-id会优先使用uni-config-center内添加的配置 +- 如果批量上传后报“请在公用模块uni-id的config.json或init方法中内添加配置项”,请重新上传一次`uni-id` + +**uni-id配置优先级** + +1. `uniID.init`、`uniID.createInstance`传入的配置(此配置不会对clientDB依赖的uni-id生效,不推荐使用) +2. uni-config-center内配置的`uni-id/config.json`(推荐使用的配置方式) +3. uni-id插件下配置的config.json(已不推荐使用的配置方式) + +以上三个配置不会进行合并,优先级高的先生效 + +##### 忽略用户名邮箱大小写@case-sensitive + +> uni-id 3.1.0及以上版本 + +uni-id 3.1.0版本主要有以下两个调整 + +1. 自此版本起会对所有接口中的用户名、邮箱、密码进行前后去空格。 + +2. 此版本之前uni-id并未忽略用户名及邮箱的大小写。这样导致了一些问题,比如用户在手机上登录不小心就会使用首字母大写的用户名或邮箱,这样就会登录失败,影响用户体验。很多应用/网站的登录都是忽略大小写的,为此uni-id在3.1.0版本起调整为默认忽略用户名、邮箱的大小写。实现方式为将用户名、邮箱均存储为小写,用户输入用户名邮箱时也转化为小写进行匹配 + +**注意** + +- 此调整兼容旧版本,以登录接口为例,优先匹配用户输入用户名对应的账号,如果不存在则匹配全小写用户名对应的账号(uni-id内部进行处理实际不会增加数据库读写次数) +- 新注册用户会将用户名/邮箱存储为全小写格式,老用户可能还存在包含大写字母的邮箱及用户名 + +##### 补齐用户dcloud_appid字段@makeup-dcloud-appid + +此调整详情见:[隔离不同端用户](uniCloud/uni-id.md?id=isolate-user) + +> uni-id3.3.0以下版本升级到3.3.0及以上版本时,需要参照本章节补齐用户数据 + +uni-id在3.3.0提供了根据客户端appid(项目manifest.json内配置的DCloud Appid)隔离不同用户的功能,旧版本的uni-id在注册用户时并未将当前客户端的appid存储在用户的记录内,更新到新版后这些没有dcloud_appid字段的用户和之前一样可以登录所有端。开发者使用云函数本地运行可以自行对用户数据进行修补,为用户创建dcloud_appid字段 + +**更新后用户将只允许登录与自己数据库记录内匹配的端** + +云函数示例代码如下: + +**注意:如果要更新的记录很多可能会超时失败,此时无需重试等待数据库自行完成更新即可** + +**如果仅有一端,将所有用户的数据更新为同一个dcloud_appid即可,例:** + +```js +exports.main = async function() { + const db = uniCloud.database() + const userCollection = db.collection('uni-id-users') + const res = await userCollection.where({ + dcloud_appid: db.command.exists(false) // 更新所有不存在dcloud_appid字段的用户 + }).update({ + dcloud_appid: ['你项目内manifest.json里面的DCloud Appid,__UNI_xxxx形式'] // 注意这里是个数组,务必正确填写 + }) + return res +} +``` + +**如果之前就有区分不同端的用户,可以将自己区分用户的条件加上再进行更新,例:** + +```js +// 更新教师端用户的云函数 +exports.main = async function() { + const db = uniCloud.database() + const userCollection = db.collection('uni-id-users') + const res = await userCollection.where({ + user_type: 'teacher', // 教师端用户 + dcloud_appid: db.command.exists(false) + }).update({ + dcloud_appid: ['教师端项目内manifest.json里面的DCloud Appid,__UNI_xxxx形式'] // 注意这里是个数组,务必正确填写 + }) + return res +} + +// 更新学生端用户的云函数 +exports.main = async function() { + const db = uniCloud.database() + const userCollection = db.collection('uni-id-users') + const res = await userCollection.where({ + user_type: 'student', // 学生端用户 + dcloud_appid: db.command.exists(false) + }).update({ + dcloud_appid: ['学生端项目内manifest.json里面的DCloud Appid,__UNI_xxxx形式'] // 注意这里是个数组,务必正确填写 + }) + return res +} +``` + +**如果允许用户在多个端登录需要将多端的DCloud Appid都传进来,例:** + +```js +exports.main = async function() { + const db = uniCloud.database() + const userCollection = db.collection('uni-id-users') + const res = await userCollection.where({ + dcloud_appid: db.command.exists(false) // 更新所有不存在dcloud_appid字段的用户 + }).update({ + dcloud_appid: ['DCloud Appid1','DCloud Appid1'] // 注意这里是个数组,务必正确填写 + }) + return res +} +``` + +# 从老版uni-id公共模块升级到uni-id-pages + +在HBuilderX 3.5之前,DCloud提供了一个公共模块[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)(注意别和uni-id-common混淆)和一个示例性云函数uni-id-cf(集成在uni-starter和uni-admin中)。 + +老的公共模块[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)是一个大而全的账户管理公共模块,体积太大,不适合被其他云函数引用。比如某个业务云函数需要校验用户token,引用的uni-id公共模块还包含了忘记密码的代码,很浪费资源。 + +从HBuilder 3.5起,[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)和uni-id-cf都将被淘汰,不再更新。老的公共模块[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)被拆开,变成了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](https://ext.dcloud.net.cn/plugin?id=2116)和uni-id-cf,官方正在升级中,将其中的用户管理升级为uni-id-pages。 + + +## 自uni-id公共模块升级到uni-id-common + uni-id-co@m-to-co + + +此次升级做了大幅改动,多数接口自公共模块中移除,改为由uni-id-co实现。仅创建token、刷新token、校验token接口保留在uni-id公共模块内。除接口调整外,uni-id体系(包含uni-id公共模块、uni-id-co)还有以下调整: + +- 配置文件内passwordErrorLimit、passwordErrorRetryTime不再生效,功能和自动检验是否需要验证码冲突 +- 彻底移除bindTokenToDevice逻辑及配置 +- 彻底移除removePermissionAndRoleFromToken逻辑及其配置,token内一定会缓存用户角色权限 +- 不再将token存储在用户表内 +- 为用户添加valid_token_date字段,**修改密码、重置密码、封禁用户场景下更新此值,刷新token时进行验证,如果token创建时间小于此时间戳,将等同于token已过期** +- 为用户添加third_party字段,用于缓存用户在三方平台的token、sessionKey等信息 +- 用户名、邮箱入库时会转化为全小写 +- `preferedAppPlatform`、`preferedWebPlatform`不再生效,uni-id内部会使用`app`代替`app-plus`、使用`web`代替`h5` +- wx_openid字段下平台名称调整,详见下方`字段名调整`相关说明 +- qq_openid字段下平台名称调整,详见下方`字段名调整`相关说明 + +### 字段名调整 + +uni-id升级为uni-id-co + uni-id-common需要对个别字段进行重命名,直接运行下面的云函数即可重命名改动的字段。另外还需要修改一下索引,删除旧字段的索引,增加新字段的索引。建议在凌晨或其他低峰段操作,避免数据库操作耗时过久影响线上用户,如果有停服逻辑也可对系统短暂停服再操作。(在实际数据测试中,50000条用户记录重命名字段耗时约5秒钟) + +```js +'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 {} +}; + +``` + +如何从老版uni-id公共模块和uni-id-cf的系统升级到uni-id-pages。 +然后老版uni-id文档开头要声明不再更新,推荐升级,指向uni-id-pages的升级章节 diff --git a/docs/uniCloud/uni-id-summary.md b/docs/uniCloud/uni-id-summary.md new file mode 100644 index 000000000..358301928 --- /dev/null +++ b/docs/uniCloud/uni-id-summary.md @@ -0,0 +1,1033 @@ +本文档适用于`uni-id 4.0.0`及以上版本,旧版本文档请访问:[uni-id 3.x.x 文档](uniCloud/uni-id-3.md) + +## 概述 + +99%的应用,都要开发用户注册、登录、发送短信验证码、修改密码、密码加密保存、密码防探测、token管理、注册用户统计等众多功能,从前端到后端都需要。 + +为什么不能有一个开源的通用项目,避免大家的重复开发呢? + +`uni-id`应需而生。 + +`uni-id`为`uniCloud`开发者提供了开源、易用、安全、丰富、可扩展的用户管理框架。 + +[clientDB](uniCloud/clientDB)、[DB Schema](uniCloud/schema)、[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057)、[uniCloud admin](uniCloud/admin),这些产品都基于`uni-id`的账户体系。可以说`uni-id`是uniCloud不可或缺的基础能力。 + +## 组成部分 + + +1. 云数据库 + +十几张用户相关的[opendb数据表](uniCloud/opendb),如[uni-id-users](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-users/collection.json)、[uni-id-roles](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-roles/collection.json)、[uni-id-permissions](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-permissions/collection.json)。 + +主表为 `uni-id-users` 表,保存用户的基本信息。扩展字段有很多,如实名认证数据、工作履历数据。由于MongoDB的特性,开发者可以自由扩展字段。 + +所有`uni-id`的数据表,不管在HBuilderX中新建DB Schema还是在uniCloud web控制台新建表的界面上,都可以选择模板直接建好。 + +2. 云端核心[公共模块](https://uniapp.dcloud.io/uniCloud/cf-common.html) [uni-id-common](uniCloud/uni-id-common.md) + +uni-id-common公共模块包含了账户体系服务端的核心权限、token管理,内置在每个uniCloud服务空间里。 + +uniCloud众多功能(如DB Schema的权限、uni-id-co)都依赖uni-id-common。如果开发者想要在自己的云函数里校验前端用户token,也需要引用uni-id-common公共模块。 + +uni-id-common这个uni_modules包括了4张opendb表,用户表uni-id-users、权限表uni-id-permissions、角色表uni-id-roles、用户日志表uni-id-log。 + +3. 云端一体页面模板 [uni-id-pages](uniCloud/uni-id-pages) + +基于uni-id-common,DCloud还提供了一组完整的前端页面和后端[云对象](https://uniapp.dcloud.io/uniCloud/cloud-obj.html) `uni-id-co`,合称uni-id-pages。 + +uni-id-pages的功能包括:用户注册(含用户协议、隐私协议)、退出、修改密码、忘记密码等各种功能,同时适配PC宽屏和各种手机平台(App、H5、小程序)。 + +此外,DCloud的其他产品也为uni-id提供了众多支持: +- [uni-admin后台管理框架](https://uniapp.dcloud.io/uniCloud/admin.html),为uni-id提供了现成的用户、角色、权限的后台管理功能,以及注册用户统计报表。 +- uni-app框架内置的uniCloud客户端SDK,自动处理了uni-id的token的网络传输(uni-app 2.7.13+版本)、续期(uni-app 3.4.13 +版本),也就是说开发者无需自己管理token了。 + +以上全部是开源的。 + +**历史遗留** + +在HBuilderX 3.5之前,DCloud提供了一个公共模块[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)(注意不叫uni-id-common)和一个示例性云函数uni-id-cf(集成在uni-starter和uni-admin中)。 + +老的公共模块uni-id是一个大而全的账户管理公共模块,体积太大,不适合被其他云函数引用。比如某个业务云函数需要校验用户token,引用的uni-id公共模块还包含了忘记密码的代码,很浪费资源。 + +在云对象发布之前,DCloud基于云函数方式提供了uni-id-cf。但在HBuilderX 3.5 以后,推荐使用基于云对象的[uni-id-pages](uniCloud/uni-id-pages),代码更简单清晰。 + +从HBuilder 3.5起,[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)和uni-id-cf都将被淘汰,不再更新。老的公共模块uni-id被拆开,变成了[uni-id-common](uniCloud/uni-id-common)公共模块和uni-id-co云对象。 + +uni-id-common很精简,只包括token和权限,适合被所有云函数引用。 + +uni-id-co则是一个更加比uni-id-cf更完善和规范的用户管理的云对象。 + +老版升级指南,[详见](uniCloud/uni-id-pages?id=m-to-co) + +## uni-id 对开发者的价值 + +1. 节省了大量重复劳动 +2. 降低门槛,前端开发者无需研究数据库如何设计、账户安全如何保障 +3. 多系统打通用户和上下游协同 + +关于第三点,着重强调下。 + +一个应用,往往需要集成多个功能模块。比如一个电商应用,需要一个基本电商模板,还需要客服聊天模板、统计看板模板。 + +在插件市场,每类模板插件都能找到,但他们如果不是基于同一套用户体系设计,就很难整合。 + +所有uniCloud的标准应用,都基于`uni-id`来做。`uni-id`公共模块自动内置在每个服务空间里的。 + +有了统一的账户规范,并且围绕这套账户规范,有各种各样插件,那么开发者可以随意整合这些插件,让数据连同。 + +规范,还可以让上下游充分协同。插件市场会出现各种数据迁移插件,比如把从discuz里把用户迁移到`uni-id`中的插件,相信围绕这套规范的产业链会非常活跃。 + +事实上,[clientDB](uniCloud/clientDB)、[DB Schema](uniCloud/schema)、[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057)、[uniCloud admin](uniCloud/admin)等重要uniCloud产品,以及插件市场上各种优秀的轮子,都是基于`uni-id`的。 + +## 现状和未来 + +`uni-id`已完成的功能: + +- 注册、登录、发送短信验证码、密码加密保存、修改密码、重置密码、头像管理、token管理、rbac权限角色体系、用户邀请裂变、用户签到、日志记录、账户防刷 + +关于登录方式,目前已实现 + +- 账户密码登录 +- 手机号+短信验证码登录 +- App手机号一键认证,免验证码 +- 三方登录:App中的微信登录和Apple ID和QQ登录、微信小程序中的微信登录、支付宝小程序中的支付宝账户登录 、QQ小程序中的QQ登录 + +关于还缺少的部分,哪些DCloud在完善,哪些希望开发者给共同完善开源项目,计划与边界公布如下: + +1. 无计划做的 + +百度、快手、快应用等小程序的登录,以及微博等App端的登录。 + +2. 有计划做但不是近期计划 + +web的微信扫码登录、字节小程序登录、facebook等海外主流社交账户登录、邮箱验证集成、活体检测。 + +上述功能均欢迎其他开发者在开源项目上提交pr,或在插件市场发布插件,共同完善`uni-id`。 + +其他方面,各种常见开源项目如discuz、wordPress、ecshop的用户导入插件,不属于`uni-id`主工程,欢迎开发者单独提交插件到插件市场。 + +# 快速上手@start + +uni-id-common的插件市场地址为:[uni-id-common插件](【待定】)。但一般不需要单独下载这个插件,但更新uni-id-common公共模块时需要从这里下载更新。 + +一般推荐直接使用uni-starter项目模板来开始开发,或者在新项目里导入uni-id-pages页面模板来使用。 + +uni-id云端的配置是依赖[uni-config-center](/uniCloud/uni-config-center)公用模块的,在工程目录uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json。(如未安装uni-config-center需安装,如缺少目录需手动创建) + +uni-id云端同时依赖了公共模块[uni-captcha](https://ext.dcloud.net.cn/plugin?id=4048),这个功能模块负责生成和校验验证码,进行人机验证。 + +体验uni-id需保证uniCloud服务空间至少有数据表`uni-id-users`、`opendb-verify-codes`(验证码表) + +使用uni-id,首先需要确定2件事: + +1. 确定注册登录方式 + +你的应用采用什么方式注册登录?比如用户名密码、手机号+短信验证码、或者微信登录。 + +很多登录方式涉及三方服务,需要开通[短信验证码服务](uniCloud/send-sms)、开通[App一键登录](https://uniapp.dcloud.net.cn/univerify.html)、或者向微信等申请登录的appid和appsecret信息。 + +申请开通相关服务后,需要把配置信息填写在云端配置文件config.json中。 + +2. 配置各项Secret + +账户如果涉及密码,那么需要配置`passwordSecret`,账户的密码会根据`passwordSecret`使用sha1摘要加密算法以不可逆的方式存储在数据库中。 + +配置`tokenSecret`是为了防止token被第三方解密,模拟用户身份。 + +千万不要使用默认的passwordSecret和tokenSecret,会造成系统安全隐患。 + +云端的config.json还有各种配置,详见下个章节。前端的配置请参考uni-id-pages的文档。 + +# 云端配置config.json的说明@config + +uni-id的云端配置文件在uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json中。 + +注意: + +- **config.json是一个标准json文件,不支持注释** +- 如果不希望使用config.json初始化而是想自行传入参数(一般不推荐这么做),可以使用`createInstance`方法[uniID.createInstance](uniCloud/uni-id.md?id=create-instance) + +> 在云函数URL化的场景无法获取客户端平台信息,可以在调用uni-id相关接口之前(推荐在云函数入口)通过修改context.PLATFORM手动传入客户端平台信息 + +例: + +```js +exports.main = async (event, context) => { + context.PLATFORM = 'app-plus' +} +``` + +配置项: + ++ `passwordSecret`为用于加密密码入库的密钥 ++ `tokenSecret`为生成token需要的密钥 ++ `tokenExpiresIn`token有效期,以秒为单位 ++ 如果使用`sendSmsCode`接口发送短信需要前往[https://dev.dcloud.net.cn/uniSms](https://dev.dcloud.net.cn/uniSms)充值短信额度,配置`config.json`的`service`字段,字段说明见下方示例 ++ 另外可以按照客户端平台进行不同的配置,参考下面示例 + +**下面的配置文件中所有时间的单位都是秒** + +> !!!重要!!! passwordSecret与tokenSecret十分重要,切记妥善保存(不要直接使用下面示例中的passwordSecret与tokenSecret)。修改passwordSecret会导致老用户使用密码无法登录,修改tokenSecret会导致所有已经下发的token失效。如果重新导入uni-id切勿直接覆盖config.json相关配置 + +```json +// 如果拷贝此内容切记去除注释 +{ + "passwordSecret": "passwordSecret-demo", // 数据库中password字段是加密存储的,这里的passwordSecret即为加密密码所用的密钥,注意修改为自己的密钥,使用一个较长的字符串即可 + "tokenSecret": "tokenSecret-demo", // 生成token所用的密钥,注意修改为自己的,使用一个较长的字符串即可 + "tokenExpiresIn": 7200, // 全平台token过期时间,未指定过期时间的平台会使用此值 + "tokenExpiresThreshold": 600, // 新增于uni-id 1.1.7版本,checkToken时如果token有效期小于此值且在有效期内则自动获取新token,请注意将新token返回给前端保存,如果不配置此参数则不开启自动获取新token功能 + "autoSetInviteCode": false, // 是否在用户注册时自动设置邀请码,默认不自动设置 + "forceInviteCode": false, // 是否强制用户注册时必填邀请码,默认为false + "app": { // 如果你使用旧版本uni-id公共模块而不是uni-id-common这里可能配置的是app-plus,务必注意调整为app + "tokenExpiresIn": 2592000, + "oauth": { + // App微信登录所用到的appid、appsecret需要在微信开放平台获取,注意:不是公众平台而是开放平台 + "weixin": { + "appid": "weixin appid", + "appsecret": "weixin appsecret" + }, + // App QQ登录所用到的appid、appsecret需要在腾讯开放平台获取,注意:不是公众平台而是开放平台 + "qq": { + "appid": "qq appid", + "appsecret": "qq appsecret" + }, + "apple": { // 使用苹果登录时需要 + "bundleId": "your bundleId" + } + } + }, + "web": { // 如果你使用旧版本uni-id公共模块而不是uni-id-common这里可能配置的是h5,务必注意调整为web + "tokenExpiresIn": 14400, + }, + "mp-weixin": { + "tokenExpiresIn": 259200, + "oauth": { + // 微信小程序登录所用的appid、appsecret需要在对应的小程序管理控制台获取 + "weixin": { + "appid": "weixin appid", + "appsecret": "weixin appsecret" + } + } + }, + "mp-qq": { + "tokenExpiresIn": 259200, + "oauth": { + // QQ小程序登录所用的appid、appsecret需要在对应的小程序管理控制台获取 + "qq": { + "appid": "qq appid", + "appsecret": "qq appsecret" + } + } + }, + "mp-alipay": { + "tokenExpiresIn": 259200, + "oauth": { + // 支付宝小程序登录用到的appid、privateKey请参考支付宝小程序的文档进行设置或者获取,https://opendocs.alipay.com/open/291/105971#LDsXr + "alipay": { + "appid": "alipay appid", + "privateKey": "alipay privateKey", // 私钥 + "keyType": "PKCS8" // 私钥类型,如果私钥类型不是PKCS8,需要填写此字段,否则会出现“error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag”错误 + } + } + }, + "service": { + "sms": { + "name": "your app name", // 应用名称,对应短信模版的name + "codeExpiresIn": 180, // 验证码过期时间,单位为秒,注意一定要是60的整数倍 + "smsKey": "your sms key", // 短信密钥key,开通短信服务处可以看到 + "smsSecret": "your sms secret", // 短信密钥secret,开通短信服务处可以看到 + "scene": { + "bind-mobile": { // 对绑定手机号场景的配置 + "templateId": "your template id", // 绑定手机号使用的短信验证码模板 + "codeExpiresIn": 240 // 绑定手机号验证码过期时间 + } + } + }, + "univerify": { + "appid": "your appid", // 当前应用的appid,使用云函数URL化,此项必须配置 + "apiKey": "your apiKey", // apiKey 和 apiSecret 在开发者中心获取,开发者中心:https://dev.dcloud.net.cn/uniLogin/index?type=0,文档:https://ask.dcloud.net.cn/article/37965 + "apiSecret": "your apiSecret" + } + } +} +``` + +**关于token自动刷新** + +tokenExpiresThreshold用于指定token还有多长时间过期时自动刷新token。 + +例:指定`tokenExpiresThreshold:600,tokenExpiresIn:7200`,token过期时间为2小时,在token有效期不足10分钟时自动刷新token + +在token还有5分钟过期时调用checkToken接口会返回新的token和新的token的过期时间(新token有效时间也是2小时)。 + +# token令牌 + +首先解释下token的概念。token是服务器颁发给客户端的一个令牌。 + +用户在客户端登录时,云端通过登录接口对用户的用户名+密码,或者手机号+验证码进行校验,校验通过后服务器会给客户端下发一个token(就是根据tokenSecret生成的一串加密字符串),并同时给出有效期。 + +客户端把这个token保存在storage中,然后每次联网请求服务器时,都带上这个token。服务器解密这个token,通过这个token认定客户端的身份。 + +这样就避免了客户端每次请求服务器,都需要再传输一次用户名和密码。 + +这是业内通行的设计。 + +在传统开发下,客户端和服务器各自需要为了token做很多事情。在uni云端一体下,开发者无需操心,只需要在uni-id云端config.json中配置好token的secret和有效期即可。剩余的工作都被自动处理了。 + +uni-id云端会在login方法成功后自动返回token,uni-app前端框架会自动识别并保存这个token在storage中(uni-id-token),在前端每次连接uniCloud(不管是clientDB、callfunction、云对象调用),都会自动带上这个token。 + +云函数和云对象都提供了获取和校验token的方法,在uni-id相关业务中,校验token的代码都已经写好。 + +包括token快到期时的自动续期,开发者只需在config.json中配置好临近多久自动续期,续期的代码也无法开发者编写,框架已经内置。 + +注:不同平台的token有效期一般不一样,app有效期较长,web有效期较短。每个平台的有效期都可以单独在config.json里配置。 + +# 用户角色权限@rbac + +为什么需要角色权限管理? +- 企业管理系统,比如[uniCloud admin](/uniCloud/admin),除了超级管理员,不同账号通常需根据职位、责任设定不同的系统权限。比如部门管理员、Hr。 +- [clientDB](/uniCloud/database)允许前端直接操作数据库,但部分字段应该是系统计算或管理员设置的,比如文章的阅读数、收藏数及是否加精置顶,这些字段不允许普通用户在前端通过clientDB直接修改,此时也需要通过权限控制来保证系统的安全稳定。 + +`uni-id`基于经典的RBAC模型实现了角色权限系统。 + +## RBAC模型简介 + +RBAC:Role-Based Access Control,基于角色的访问控制。 + +其基本思想:对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有权限。 + +![](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/431878b0-0ca0-11eb-8a36-ebb87efcf8c0.png) + +这样做的好处是,增强系统管理的扩展性,对于批量用户的权限变更,仅需变更该批用户角色对应权限即可,而无需对该批每个用户变更权限。 + +这个模型有三个关键名词:用户、角色、权限: +- 用户:使用系统的人,一个用户可以同时有多个角色,比如内容审核员、比如部门管理员 +- 角色:权限的集合,一个角色可以有多个权限。比如内容审核员这个角色,有隐藏帖子权限、有帖子加精权限;而部门管理员这个角色,有给本部门新增员工权限、有删除本部门员工的权限。 +- 权限:数据权限或业务权限,例如:删除用户、帖子加精等 + +用户、角色、权限都存在数据库了,都可以动态创建和修改。当权限对应的代码实现完成后,用户的新入、退出、角色升迁都无需再修改代码,在uni-admin后台的web界面可以由运维人员可视化的给每个用户调整角色、给每个角色调整权限。 + +## 用户 + +用户信息存储在`uni-id-users`表中,然后通过`role`字段保存该用户所拥有的所有角色ID,角色ID即角色表(`uni-id-roles`表)中的`role_id`字段,注意不是`_id`字段。 + +``` json +// uni-id-users 表 +{ + { + "_id":"5f8428181c229600010389f6", + "username":"张三", + "email":"zhangsan@dcloud.io", + "role":[ + "USER_ADMIN", + "NOTICE_ADMIN" + ], + "created_date":1602495783272 + } +} +``` + +>Tips:将用户角色设计为用户表的字段,而没有新建`用户角色关联表`的原因:避免mongodb在跨表查询时的性能开销 + +## 角色 + +角色信息存储在`uni-id-roles`表中 + +| 字段 | 类型 | 必填| 描述 | +| ---------- | --------- | ----| --------------------------------------| +| \_id | Object ID | 是 | 系统自动生成的Id | +| role_id | String | 是 | 角色唯一标识 | +| role_name | String | 否 | 角色名,展示用 | +| permission | Array | 是 | 角色拥有的权限列表 | +| comment | String | 否 | 备注 | +| created_date| Timestamp | 是 | 角色创建时间 | + +其中: +- `role_id`为角色标志,全局唯一,可用于clientDB中的权限控制,建议按照语义化命名,例如:`USER_ADMIN`表示人事管理、`NOTICE_ADMIN`表示公告管理 +- `permission`为数组类型,存储该角色拥有的所有权限ID,权限ID即权限表(`uni-id-permissions`表)中的`permission_id`字段,注意不是`_id`字段 + +如下为示例: + +```js +{ + { + "_id":"5f8428181c229600010389f6", + "role_id":"USER_ADMIN", + "role_name":"人事管理", + "permission":[ + "USER_ADD", + "USER_EDIT", + "USER_DEL" + ], + "created_date":1602495783272 + }, + { + "_id":"5f842836d8daea0001906785", + "role_id":"NOTICE_ADMIN", + "role_name":"公告管理", + "permission":[ + "NOTICE_ADD", + "NOTICE_EDIT", + "NOTICE_DEL" + ], + "created_date":1602495784372 + } +} +``` + +如下是角色在clientDB中的配置示例: + +```js +// uni-id-users.schema.json +{ + "permission": { + "update":"doc._id == auth.uid || 'USER_ADMIN' in auth.role" //用户自己或人事管理员可执行用户表的.update操作 + } +} +``` + +>Tips1:uni-id中`admin`为超级管理员角色,uni-clientDB也基于同样的策略;如果用户角色包含`admin`,则该用户就拥有所有数据表的全部权限。 + +>Tips2:出厂时可内置常用角色,也可上线后由运营人员动态创建角色。 + +## 权限 + +权限信息在`uni-id-permissions`表中,表结构定义如下: + +| 字段 | 类型 | 必填| 描述 | +| ---------- | --------- | ----| --------------------------------------| +| \_id | Object ID | 是 | 系统自动生成的Id | +| permission_id | String | 是 | 权限唯一标识 | +| permission_name | String | 否 | 权限名,展示用 | +| comment | String | 否 | 备注 | +| created_date | Timestamp | 是 | 权限创建时间 | + +其中,`permission_id`为权限标志,全局唯一,可用于clientDB中的权限配置,建议按照语义化命名,例如:`USER_DEL`、`BRANCH_ADD`。**权限总数量不得超过500** + +如下为示例内容: + +```js +{ + { + "_id":"5f8428181c229600010389f6", + "permission_id":"USER_EDIT", + "permission_name":"修改用户", + "created_date":1602495783272 + }, + { + "_id":"5f842836d8daea0001906785", + "permission_id":"USER_DEL", + "permission_name":"删除用户", + "created_date":1602495784372 + } +} +``` + +如下是权限在clientDB中的配置示例: + +```js +// uni-id-users.schema.json +{ + "permission": { + "update":"doc._id == auth.uid || 'USER_EDIT' in auth.permission" //用户自己或有`USER_EDIT`权限的用户,可执行用户表的.update操作 + } +} +``` + +>Tips1:建议代码交付时内置所有权限,方便clientDB中的权限配置和调整。 + +## 其他说明 + +uni-id将用户的角色权限缓存在token内。详情参考:[缓存角色权限](uniCloud/uni-id?id=cache-permission-in-token)。 + +如下是通过token判断权限的简单示例: + +```js +// 简单的权限校验示例 +function hasPermission(token, permission) { + const { + uid, + role, + permission + } = await uniID.checkToken(token) + return role.includes('admin') || checkTokenRes.permission.includes(permission) // admin用户的permission为空数组,但是拥有所有权限 +} +``` + +注意:**在uniCloud admin中,封装了可视化的用户、权限、角色的管理,新增删除修改均支持。**无需自己维护。[详见](https://uniapp.dcloud.net.cn/uniCloud/admin?id=mutiladmin) + +# 数据库结构@db-schema + +`uni-id`的所有数据表,都在[opendb](https://gitee.com/dcloud/opendb/)规范中。 + +在unicloud [web控制台](https://unicloud.dcloud.net.cn/) 新建数据表时,可以从`uni-id`的模板分类里找到下面的表,并一键创建这些表。 + +## 用户表@user-table + +表名:`uni-id-users` + +| 字段 | 类型 | 必填 | 描述 | +| ---------------- | --------- | ---- | ------------------------------------------- | +| \_id | Object ID | 是 | 存储文档 ID(用户 ID),系统自动生成 | +| username | String | 否 | 用户名,不允许重复 | +| password | String | 否 | 密码。加密存储 | +| nickname | String | 否 | 用户昵称 | +| gender | Integer | 否 | 用户性别:0 未知 1 男性 2 女性 | +| role | Array | 否 | 用户角色列表,由role_id组成的数组 | +| status | Integer | 是 | 用户状态:0 正常,1 禁用,2 审核中,3 审核拒绝,4 已注销 | +| dcloud_appid | Array | 否 | 允许登录的客户端的appid列表,不同应用同时复用一个user表时适用,比如 司机端和乘客端是2个appid,在登陆时可以隔离,[详见](uniCloud/uni-id?id=isolate-user)| +| mobile | String | 否 | 手机号码 | +| mobile_confirmed | Integer | 否 | 手机号验证状态:0 未验证 1 已验证,未验证用户不可登录 | +| email | String | 否 | 邮箱地址 | +| email_confirmed | Integer | 否 | 邮箱验证状态:0 未验证 1 已验证,未验证用户不可登录 | +| avatar | String | 否 | 头像地址 | +| wx_unionid | String | 否 | 微信unionid | +| wx_openid | Object | 否 | 微信各个平台openid | +| ali_openid | String | 否 | 支付宝平台openid | +| apple_openid | String | 否 | 苹果登录openid +| comment | String | 否 | 备注 | +| realname_auth | Object | 否 | 实名认证信息 | +| register_date | Timestamp | 否 | 注册时间 | +| register_ip | String | 否 | 注册时 IP 地址,`uni-id 3.3.14`起移至register_env内 | +| last_login_date | Timestamp | 否 | 最后登录时间 | +| last_login_ip | String | 否 | 最后登录时 IP 地址 | +| login_ip_limit | Array | 否 | 登录 IP 限制 | +| inviter_uid | Array | 否 | 邀请人uid,按层级从下往上排列的uid数组,即第一个是直接上级| +| my_invite_code | String | 否 | 用户自己的邀请码 | +| register_env | Object | 否 | 用户注册时的环境信息,新增于`uni-id 3.3.14` | + +**注意** + +- 最后登录时间、IP,并非只有登录操作会修改,token刷新时也会修改最后登录时间、ip。应用启动时若token有效则不会触发登录行为,也不会更新本值。 + +**wx_openid字段定义** + +> opendb中uni-id-users表1.0.0调整为下面的结构,uni-id-co使用此标准。如何处理旧数据请参考:[自uni-id升级为uni-id-co+uni-id-common](uniCloud/uni-id-summary.md?id=m-to-co) + +| 字段 | 类型 | 必填 | 描述 | +| ------- | ------| ---- | -------- | +| app | String| 否 | app平台微信openid | +| mp | String| 否 | 微信小程序平台openid | +| h5 | String| 否 | 微信网页应用openid | +| web | String| 否 | 微信公众号应用openid | + +**realNameAuth 扩展字段定义** +该字段存储实名认证信息,子节点说明如下。 + +| 字段 | 类型 | 必填 | 描述 | +| --------------- | --------- | ---- | --------------------------------------------------- | +| type | Integer | 是 | 用户类型:0 个人用户 1 企业用户 | +| auth_status | Integer | 是 | 认证状态:0 未认证 1 等待认证 2 认证通过 3 认证失败 | +| auth_date | Timestamp | 否 | 认证通过时间 | +| real_name | String | 否 | 真实姓名/企业名称 | +| identity | String | 否 | 身份证号码/营业执照号码 | +| id_card_front | String | 否 | 身份证正面照 URL | +| id_card_back | String | 否 | 身份证反面照 URL | +| id_card_in_hand | String | 否 | 手持身份证照片 URL | +| license | String | 否 | 营业执照 URL | +| contact_person | String | 否 | 联系人姓名 | +| contact_mobile | String | 否 | 联系人手机号码 | +| contact_email | String | 否 | 联系人邮箱 | + +**job 扩展字段定义** + +| 字段 | 类型 | 必填 | 描述 | +| ------- | ------ | ---- | -------- | +| company | String | 否 | 公司名称 | +| title | String | 否 | 职位 | + +**register_env字段定义** + +**注意:调用addUser添加的用户无此字段** + +| 字段 | 类型 | 必填 | 描述 | +|-- |-- |-- |-- | +|appid |String|否 |注册时的客户端appId | +|uni_platform |String |否 |注册时的客户端平台,如h5、app、mp-weixin等 | +|os_name |String |否 |注册时的客户端系统名,ios、android、windows、mac、linux | +|app_name |String |否 |注册时的客户端名称 | +|app_version |String |否 |注册时的客户版本 | +|app_version_code |String |否 |注册时的客户版本号 | +|channel |String |否 |注册时的客户端启动场景(小程序)或应用渠道(app) | +|client_ip |String |否 |注册时的客户端IP | + + +用户集合示例: + +``` +{ + "_id": "f2a60d815ee1da3900823d45541bb162", + "username": "姓名" + "password": "503005d4dd16dd7771b2d0a47aaef927e9dba89e", + "status":0,//用户状态:0正常 1禁用 2审核中 3审核拒绝 + "mobile":"", + "mobile_confirmed":0, //手机号是否验证,0为未验证,1为已验证 + "email":"amdin@domain.com", + "email_confirmed":0, //邮箱是否验证,0为未验证,1为已验证 + "avatar":"https://cdn.domain.com/avatar.png" + "register_ip": "123.120.11.128", //注册IP + "last_login_ip": "123.120.11.128", //最后登录IP + +} +``` + +**如果需要管理多系统的用户,建议使用type在uni-id-users表内进行区分,不要分多个表** + +### 用户表索引使用注意@uni-id-users-indexes + +目前opendb内提供的uni-id-users表包含完整的索引,数据库在索引量多且频繁更新的情况下可能会出现写入缓慢的情况,因此推荐开发者在使用uni-id-users表时可以删除没有用到的索引。 + +例:项目内只使用了微信登录,不使用其他登录方式,可以只保留`wx_unionid、wx_openid.mp-weixin、wx_openid.app-plus`这些账号相关的索引,删除其他登录方式的索引(比如username、mobile) + +不了解索引请参考:[索引](/uniCloud/db-index.html) + +## 验证码表 + +表名:`opendb-verify-codes` + +**uni-id 2.0.0版本以前,使用的表名为uni-verify,2.0.0+起改为新表名** + +该表的前缀不是uni-id,意味着该表的设计用途是通用的,不管是uni-id的手机号验证码,或者支付等关键业务需要验证码,都使用此表。 + +每条验证信息,都记录在本表中。uni-id不会自动删除本表的历史数据,数据保留有效期需要开发者自行管理,可以在云函数中设置一个定时运行来清理过期数据。 + +| 字段 | 类型 | 必填 | 描述 | +| ---------- | --------- | ---- | --------------------------------------| +| \_id | Object ID | 是 | 存储文档 ID(验证码 ID),系统自动生成| +| mobile | String | 是 | 手机号,和邮箱二选一 | +| email | String | 是 | 邮箱,和手机号二选一 | +| code | String | 是 | 验证码 | +| scene | String | 是 | 验证场景 | +| state | Integer | 是 | 验证状态:0 未验证 1 已验证 2 已作废 | +| ip | String | 是 | 请求时 IP 地址 | +| create_date | Timestamp | 是 | 创建时间 | +| expired_date | Timestamp | 是 | 验证码过期时间 | + +## 角色表 + +表名:`uni-id-roles` + +| 字段 | 类型 | 必填| 描述 | +| ---------- | --------- | ----| --------------------------------------| +| \_id | Object ID | 是 | 系统自动生成的Id | +| role_id | String | 是 | 角色唯一标识 | +| role_name | String | 否 | 角色名,展示用 | +| permission | Array | 是 | 角色拥有的权限列表 | +| comment | String | 否 | 备注 | +| created_date| Timestamp | 是 | 角色创建时间 | + +## 权限表 + +表名:`uni-id-permissions` + +| 字段 | 类型 | 必填| 描述 | +| ---------- | --------- | ----| --------------------------------------| +| \_id | Object ID | 是 | 系统自动生成的Id | +| permission_id | String | 是 | 权限唯一标识 | +| permission_name | String | 否 | 权限名,展示用 | +| comment | String | 否 | 备注 | +| created_date | Timestamp | 是 | 权限创建时间 | + +## 更多表 + +还有更多uni-id的配套数据表,可以在uniCloud [web控制台](https://unicloud.dcloud.net.cn/)新建表时选择相应模板。此处不再详述,仅罗列清单: + +- 积分表:uni-id-scores +- 地址信息表:uni-id-address +- 订单表:uni-id-base-order +- 设备表:uni-id-device +- 关注粉丝表:uni-id-followers +- 日志表:uni-id-log +- 任务表:uni-id-task +- 任务日志表:uni-id-task-log + +# 常量@constants + +## 用户状态@user-status + +对应`uni-id-users`表的status字段 + +|值 |说明 | +|-- |-- | +|0 |正常状态 | +|1 |封禁状态 | +|2 |审核中 | +|3 |审核失败 | +|4 |注销状态 | + +## log类型@log-type + +对应`uni-id-log`表的type字段 + +|值 |说明 | +|-- |-- | +|logout |登出 | +|login |登录 | +|register |注册 | +|reset-pwd |重置密码 | +|bind-mobile|绑定手机 | +|bind-weixin|绑定微信账号 | +|bind-qq |绑定QQ账号 | +|bind-apple |绑定苹果账号 | +|bind-alipay|绑定支付宝账号 | + +## 手机、邮箱验证码使用场景@sms-type + +对应`opendb-verify-codes`表短信、邮箱验证码相关记录的的scene字段 + +|值 |说明 | +|-- |-- | +|login-by-sms |短信验证码登录 | +|reset-pwd-by-sms |短信验证码重置密码 | +|bind-mobile-by-sms |短信验证码绑定手机号 | + +## 图形验证码使用场景@captcha-scene + +对应`opendb-verify-codes`表图形验证码相关记录的scene字段 + +|值 |说明 | +|-- |-- | +|login-by-pwd |用户名/手机/邮箱+密码登录 | +|login-by-sms |短信验证码登录 | +|reset-pwd-by-sms |短信验证码重置密码 | +|send-sms-code |发送短信验证码 | +|bind-mobile-by-sms |短信验证码绑定手机号 | + +# uniIdRouter自动路由@uni-id-router + +> 新增于 HBuilderX 3.5.0 + +开发者可以在项目的`pages.json`内配置需要登录的页面,登录页面路径等信息,uniCloud会自动在需要登录且客户端登录状态过期或未登录时跳转到登录页面。 + +结合以下代码及注释了解如何使用`uniIDRouter` + +```json +{ + "pages": [ + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "uni-app" + }, + "needLogin": false // 当前页面是否需要登录才可以访问,此配置优先级高于uniIDRouter下的needLogin + }, { + "path": "pages/list/list", + "style": { + "navigationBarTitleText": "uni-app" + }, + "needLogin": false + }, { + "path": "pages/detail/detail", + "style": { + "navigationBarTitleText": "uni-app" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "uni-app", + "navigationBarBackgroundColor": "#F8F8F8", + "backgroundColor": "#F8F8F8" + }, + "uniIDRouter": { + "loginPage": "pages/index/index", // 登录页面路径 + "needLogin": [ + "pages/detail/.*" // 需要登录才可访问的页面列表,可以使用正则语法 + ], + "resToLogin": true // 自动解析云对象及clientDB的错误码,如果是客户端token不正确或token过期则自动跳转配置的登录页面,配置为false则关闭此行为,默认true + } +} + +``` + +与此功能对应的有两个uniCloud客户端api,`uniCloud.onNeedLogin()`和`uniCloud.offNeedLogin()`,开发者在监听onNeedLogin事件后,框架就不再自动跳转到登录页面,而是由开发者在onNeedLogin事件内自行处理。详情参考:[uniCloud.onNeedLogin](uniCloud/client-sdk.md?id=on-need-login) + +自动跳转到登录页面时会携带uniIdRedirectUrl参数,其值为`encodeURIComponent(${跳转前的页面(包含路径和参数的完整页面地址)})`,如果希望用户登录后跳转回之前的页面,可以使用此参数实现。 + +以下为登录页面跳转到之前访问页面的简单示例: + +```html +// pages/login/login.vue + + + + +``` + +**注意** + +- pages.json内有`uniIDRouter`节点上述逻辑才会生效,自HBuilderX 3.5.0起创建空项目模板会自动配置空的`uniIDRouter`节点 + +# 错误码@errcode + +|错误码 |错误信息 |说明 | +|---- |---- |---- | +|0(数字) |成功 |- | +|uni-id-token-expired |登陆状态失效,token已过期 |- | +|uni-id-check-token-failed |token校验未通过 |- | +|uni-id-account-exists |账户已存在 |- | +|uni-id-account-not-exists |账户不存在 |- | +|uni-id-account-conflict |用户账号冲突 |可能会由开发者手动更新数据库导致,正常情况下不应出现 | +|uni-id-account-banned |此账号已封禁 |- | +|uni-id-account-auditing |此账号正在审核中 |- | +|uni-id-account-audit-failed |此账号审核失败 |- | +|uni-id-account-closed |此账号已注销 |- | +|uni-id-captcha-required |请输入图形验证码 |- | +|uni-id-password-error |用户名或密码错误 |- | +|uni-id-invalid-username |用户名不合法 |- | +|uni-id-invalid-password |密码不合法 |- | +|uni-id-invalid-mobile |手机号码不合法 |- | +|uni-id-invalid-email |邮箱不合法 |- | +|uni-id-invalid-nickname |昵称不合法 |- | +|uni-id-invalid-param |参数错误 |- | +|uni-id-param-required |缺少参数 |- | +|uni-id-get-third-party-account-failed |获取第三方账号失败 |- | +|uni-id-get-third-party-user-info-failed|获取第三方用户信息失败 |- | +|uni-id-mobile-verify-code-error |手机验证码错误或已过期 |- | +|uni-id-email-verify-code-error |邮箱验证码错误或已过期 |- | +|uni-id-admin-exists |超级管理员已存在 |- | +|uni-id-permission-error |权限错误 |- | +|uni-id-system-error |系统错误 |- | +|uni-id-set-invite-code-failed |设置邀请码失败 |- | +|uni-id-invalid-invite-code |邀请码不可用 |- | +|uni-id-change-inviter-forbidden |禁止修改邀请人 |- | +|uni-id-bind-conflict |此账号(微信、QQ、手机号等)已被绑定 |- | + +# 多个应用复用相同uni-id-user表 + +有些系统由多个子应用组成,且没有各自独立服务空间,而是需要共享一个服务空间。此时就涉及一个问题,多个应用注册的账户都在uni-id-user表中,如何有效隔离。 + +比如一个打车软件,有乘客端、司机端、管理端,都要注册账户。它们也都有自己的DCloud appID(manifest.json里第一个配置) + +uni-id-user表中有一个数组型字段`dcloud_appid`,可以存贮这个用户有权登陆哪个应用。 + +比如乘客端的appid是`__uni_111111`,司机端appid是`__uni_222222`,那么2个appid都存入`dcloud_appid`,即表示这个用户有权登录这2个应用。 + +## 隔离不同应用的用户@isolate-user + +uni-id 3.3.0版本起用户注册时会自动在用户表的记录内标记为注册应用对应的用户,如果没有单独授权登录其他应用的话则只能登录这个应用。即在乘客端应用注册的,默认只能在乘客端应用登录。 + +如何授权登录其他应用请参考:[授权、禁止用户在特定客户端应用登录](uniCloud/uni-id?id=authorize-app) + +需要注意的是客户端APPID信息是由端上传上来的,并非完全可信,尽量在入口处进行校验。例: + +```js +exports.main = async function(event, context){ + if(context.APPID !== '__UNI__xxx1') { + throw new Error('应用ID非法') + } +} +``` + +**DCloud Appid是一个很重要的配置,如无必要请勿随意更换。** + +> 不同端用户数据通过用户表的dcloud_appid字段隔离,同一个手机号、微信号也可以同时注册管理端和用户端,绑定账号同理。 + +**注意** + +- uni-id会自动在用户表每条用户记录插入`dcloud_appid`字段(此字段是一个数组,标识此用户可以在哪些端登录)。 +- 为兼容旧版本产生的旧数据,针对没有dcloud_appid字段的用户,允许登录任意端。 +- 如果用户数据库记录中`dcloud_appid`字段是一个空数组,表示当前用户不能在任何客户端登录 +- 已有dcloud_appid的用户,如果使用相同的用户标识(用户名、邮箱、手机、微信等)+ 不同的DCloud Appid登录会被判定为不同的用户,如果此时数据库没有对应的记录,会报用户不存在的错误 + +## 隔离不同应用的配置@isolate-config + +uni-id的config.json支持配置为数组,每项都是一个完整的配置,对不同的配置使用`dcloudAppid`字段进行区分(**此字段与项目内的manifest.json里面的DCloud AppId一致**),uni-id会自动根据客户端的appid来判断该使用哪套配置。如果使用云函数url化请参考:[云函数Url化时使用](uniCloud/uni-id?id=url) + +需要注意的是客户端APPID信息是由端上传上来的,并非完全可信,尽量在入口处进行校验。例: + +```js +exports.main = async function(event, context){ + if(context.APPID !== '__UNI__xxx1') { + throw new Error('应用ID非法') + } +} +``` + +**示例** + +> 数组每一项都是一个完整的配置文件,全部选项请参考:[uni-id 配置](uniCloud/uni-id-summary?id=config) + +**注意:如果允许同一账号在不同端使用相同的账号+密码登录需要将不同端的passwordSecret设置成一样的** + +```js +[{ + "dcloudAppid": "__UNI__xxxx1", // 务必替换为对应项目manifest.json内的DCloud Appid + "isDefaultConfig": true, // 默认配置标记,未匹配到dcloudAppid的情况下使用默认配置 + "passwordSecret": "passwordSecret-demo", + "tokenSecret": "tokenSecret-demo", + "tokenExpiresIn": 7200, + "tokenExpiresThreshold": 600 +}, { + "dcloudAppid": "__UNI__xxxx2", // 务必替换为对应项目manifest.json内的DCloud Appid + "passwordSecret": "passwordSecret-demo", + "tokenSecret": "tokenSecret-demo", + "tokenExpiresIn": 7200, + "tokenExpiresThreshold": 600 +}] +``` + +# 其他功能 + +## 裂变@fission + +在`config.json`内配置了`autoSetInviteCode: true`则在用户注册时会自动给设置不重复的6位邀请码 + +在`config.json`内配置了`forceInviteCode: true`则只有使用邀请码才可以注册。 + +`uni-id-co`在会产生注册行为的接口均添加了inviteCode参数,用于传递邀请码使注册用户接受邀请 + +## 修改passwordSecret@modifysecret + +**注意:通常情况下设定好passwordSecret之后不需要再进行修改,使用此功能时请务必小心谨慎** + +**说明** + +在config.json内修改passwordSecret会导致历史用户无法通过密码登录。但是某些情况下有些应用有修改passwordSecret的需求,例如刚开始使用uni-id时没有自定义passwordSecret,后续需要修改,此时可以使用uni-id 2.0.1版本新增的修改passwordSecret功能。(注意:2.0.1版本验证码表名调整为了`opendb-verify-codes`) + +**如何使用** + +下面以将passwordSecret从`passwordSecret-demo`修改为`qwertyasdfgh`为例介绍如何使用 + +```json +// 旧config.json +{ + "passwordSecret": "passwordSecret-demo" +} + +// 新config.json +{ + "passwordSecret": [{ + "version": 1, + "value": "passwordSecret-demo" + },{ + "version": 2, + "value": "qwertyasdfgh" + }] +} +``` + +如果在上面基础上再修改passwordSecret为`1q2w3e4r5t`,config.json调整如下 + +> !!!注意只有在数据库内完全没有使用某个版本(`password_secret_version`字段表示了用户密钥版本)密钥的用户才可以将此密钥从config.json内去除。没有`password_secret_version`的用户使用的是最旧版本的passwordSecret,如果存在这样的用户对应的passwordSecret也不可去除。 + +```json +// 新config.json, +{ + "passwordSecret": [{ + "version": 1, + "value": "passwordSecret-demo" + },{ + "version": 2, + "value": "qwertyasdfgh" + },{ + "version": 3, + "value": "1q2w3e4r5t" + }] +} +``` + +**原理** + +uni-id-users表内存储的password字段为使用hmac-sha1生成的hash值,此值不可逆向推出用户真实密码。所以直接修改passwordSecret会导致老用户无法使用密码登录。 + +上述修改通过密钥版本号区分新旧密钥,用户登录时如果密钥版本小于当前最新版本,会为用户更新数据库内存储的password字段,并记录当前使用的密钥版本。 + +用户对应的数据库记录内没有密钥版本的话会使用最低版本密钥进行密码校验,校验通过后为用户更新为最新版密钥对应的password并记录版本号。 + +由于是不可逆加密,理论上passwordSecret泄露不会造成用户的真实密码被泄露,自定义passwordSecret只是进一步加强安全性。 + +## 缓存角色权限@cache-permission-in-token + +使用`uni-id-common`时,token内会缓存用户的角色权限。 + +为什么要缓存角色权限?要知道云数据库是按照读写次数来收取费用的,并且读写数据库会拖慢接口响应速度。 + +**注意** + +- 由于角色权限缓存在token内,可能会存在权限已经更新但是用户token未过期之前依然是旧版角色权限的情况。可以调短一些token过期时间来减少这种情况的影响。 +- admin角色token内不包含permission,如需自行判断用户是否有某个权限,要注意admin角色需要额外判断一下,写法如下 + ```js + const { + role, + permission + } = await uniID.checkToken(event.uniIdToken) + if(role.includes('admin') || permission.includes('your permission id')) { + // 当前角色拥有'your permission id'对应的权限 + } + ``` + +## 自定义token内容@custom-token + +uni-id-common支持在token内缓存用户的角色权限。但是某些情况下开发者可能还希望缓存一些别的东西,以便在客户端能方便的访问(**注意:不可缓存机密信息到token内**)。 + +**用法** + +在`uni-config-center`模块内的uni-id插件内创建`custom-token.js`内容如下: + +```js +module.exports = async (tokenObj) => { + // tokenObj为原始token信息结构如下 + // { + // uid: 'abc', // 用户id + // role: [], // 用户角色列表 + // permission: [] // 用户权限列表,admin角色的用户权限列表为空数组 + // } + + tokenObj.customField = 'hello custom token' // 自定义token字段 + return tokenObj // 注意务必返回修改后的token对象 +} +``` + +uni-id会自动加载custom-token.js进行处理,在所有生成token的操作(包括:登录、注册、token过期自动刷新、开发者自行调用createToken)执行时自动获取新token信息,并生成token。 + +**注意** + +- 不要删除原始token内的字段 + +## 自定义国际化语言@custom-i8n + +完整词句列表参考:[uni-id中文语言包](https://gitee.com/dcloud/uni-id/blob/master/src/lang/zh-Hans.js) + +**用法** + +在`cloudfunctions/common/uni-config-center/uni-id/lang/`目录下创建`index.js`,内容示例如下: + +```js +module.exports = { + 'zh-hant': { // 语言代码 + 'alipay': '支付寶' + } +} +``` + +uni-id会自动进行语言匹配,无需额外配置 + + + diff --git a/docs/uniCloud/uni-id.md b/docs/uniCloud/uni-id.md index a02aeddee..ce8b5f1a3 100644 --- a/docs/uniCloud/uni-id.md +++ b/docs/uniCloud/uni-id.md @@ -1,3 +1,5 @@ +本文档为uni-id旧版本文档,已不再维护。新文档请查看:[uni-id文档](uniCloud/uni-id-summary.md),如何从旧版本升级到uni-id-pages请查看:[升级到uni-id-pages](uniCloud/uni-id-pages.md?id=m-to-co) + # 需求背景 99%的应用,都要开发用户注册、登录、发送短信验证码、密码加密保存、修改密码、token管理等功能,从前端到后端都需要。 -- GitLab