uni-id-summary.md 55.6 KB
Newer Older
1
本文档适用于`uni-id 4.0.0`及以上版本,需 HBuilderX 3.5.0 及以上版本。旧版本文档请访问:[uni-id 3.x.x 文档](uni-id-3.md)
雪洛's avatar
雪洛 已提交
2

3
## 需求背景
雪洛's avatar
雪洛 已提交
4

5
99%的应用,都要开发用户注册、登录、发送短信验证码、修改密码、密码加密保存、密码防探测、token管理、页面访问权限、注册用户统计等众多功能,从前端到后端都需要。
雪洛's avatar
雪洛 已提交
6 7 8 9 10 11 12

为什么不能有一个开源的通用项目,避免大家的重复开发呢?

`uni-id`应需而生。

`uni-id``uniCloud`开发者提供了开源、易用、安全、丰富、可扩展的用户管理框架。

13
[clientDB](clientDB.md)[DB Schema](schema.md)[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057)[uni-admin](admin.md),这些产品都基于`uni-id`的账户体系。可以说`uni-id`是uniCloud不可或缺的基础能力。
雪洛's avatar
雪洛 已提交
14

15
## uni-id 的价值
雪洛's avatar
雪洛 已提交
16

17 18 19
1. 节省了大量重复劳动
2. 降低门槛,前端开发者无需研究数据库如何设计、账户安全如何保障
3. 多系统打通用户和上下游协同
雪洛's avatar
雪洛 已提交
20

21
关于第三点,着重强调下。
雪洛's avatar
雪洛 已提交
22

23
一个应用,往往需要集成多个功能模块。比如一个电商应用,需要一个基本电商模板,还需要客服聊天模板、统计看板模板。
雪洛's avatar
雪洛 已提交
24

25
在插件市场,每类模板插件都能找到,但他们如果不是基于同一套用户体系设计,就很难整合。
雪洛's avatar
雪洛 已提交
26

27
所有uniCloud的标准应用,都基于`uni-id`来做。`uni-id-common`公共模块自动内置在每个服务空间里的。
雪洛's avatar
雪洛 已提交
28

29
有了统一的账户规范,并且围绕这套账户规范,有各种各样插件,那么开发者可以随意整合这些插件,让数据连同。
雪洛's avatar
雪洛 已提交
30

31
规范,还可以让上下游充分协同。插件市场会出现各种数据迁移插件,比如把从discuz里把用户迁移到`uni-id`中的插件,相信围绕这套规范的产业链会非常活跃。
雪洛's avatar
雪洛 已提交
32

33
目前插件市场上各种优秀的uniCloud轮子,几乎都是基于`uni-id`的。
雪洛's avatar
雪洛 已提交
34

35
## 功能清单
雪洛's avatar
雪洛 已提交
36

37
`uni-id`已完成的功能:
雪洛's avatar
雪洛 已提交
38

39
- 注册、登录、发送短信验证码、密码加密保存、修改密码、忘记密码、头像管理、token管理、rbac权限角色体系、页面访问权限路由控制、用户邀请裂变、用户签到、日志记录、账户防刷
雪洛's avatar
雪洛 已提交
40

41
关于登录方式,目前已实现
雪洛's avatar
雪洛 已提交
42

43 44 45 46
- 账户密码登录
- 手机号+短信验证码登录 (内置uniCloud短信能力)
- App手机号一键认证,免验证码(内置uni-app App一键登录能力)
- 三方登录:App中的微信登录、Apple ID、QQ登录;微信小程序中的微信登录;支付宝小程序中的支付宝账户登录;QQ小程序中的QQ登录
雪洛's avatar
雪洛 已提交
47

48
由于三方登录很多,DCloud没有精力全部实现,在uni-id-co中留下了空实现,欢迎开发者自行补充、提交pr或发布扩展插件,共同完善`uni-id`。。
雪洛's avatar
雪洛 已提交
49

50
后续计划:DCloud未来将内置 微信扫码登录和公众号登录、邮箱验证集成、facebook等海外主流社交账户登录、活体检测。
雪洛's avatar
雪洛 已提交
51

52
其他方面,各种常见开源项目如discuz、wordPress、ecshop的用户导入插件,不属于`uni-id`主工程,欢迎开发者单独提交插件到插件市场。
雪洛's avatar
雪洛 已提交
53

54
## 组成结构
雪洛's avatar
雪洛 已提交
55

56
`uni-id`贯穿了uni-app前端到uniCloud后端的各个环节。
雪洛's avatar
雪洛 已提交
57

58 59 60 61 62 63 64 65 66 67
|模块						|说明															|
|--							|--																|
|前端uni-app框架的相关API		|uniIdRouter页面路由、token管理客户端API						|
|前端页面uni-id-pages		|登录、注册、修改密码、忘记密码、个人中心、修改头像等前端页面	|
|网络传输自动管理用户token	|自动保存、续期token、网络自动传输token							|
|云端云对象uni-id-co			|与uni-id-pages搭配的云对象,相关业务的云端部分					|
|云端配置uni-config-center	|在uni-config-center下提供各种配置								|
|云端公共模块uni-id-common	|用于云函数或云对象集成该模块验证token身份						|
|云数据库的用户相关数据表		|uni-id-users等各种opendb数据表									|
|uni-admin					|Admin管理后台,包括用户角色权限管理、注册用户统计					|
雪洛's avatar
雪洛 已提交
68 69


70
1. 云数据库的uni-id相关表
雪洛's avatar
雪洛 已提交
71

72
数据库是一个系统的核心,uni-id首先规范化了十几张用户相关的[opendb数据表](opendb.md)
雪洛's avatar
雪洛 已提交
73

74 75 76 77 78
其中最为重要的4张opendb表,如下:
- 用户表 [uni-id-users](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-users/collection.json)
- 权限表 [uni-id-permissions](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-permissions/collection.json)
- 角色表 [uni-id-roles](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-roles/collection.json)
- 用户日志表 [uni-id-log](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-log/collection.json)
雪洛's avatar
雪洛 已提交
79

80
主表为`uni-id-users`表,保存用户的基本信息。扩展字段有很多,如实名认证数据、工作履历数据。由于MongoDB的特性,开发者可以自由扩展字段。
雪洛's avatar
雪洛 已提交
81

82
所有`uni-id`的数据表,不管在HBuilderX中新建 `DB Schema` 还是在 uniCloud web控制台新建表的界面上,都可以选择模板直接建好。
雪洛's avatar
雪洛 已提交
83

84
2. 云端公共模块uni-id-common
雪洛's avatar
雪洛 已提交
85

86
uni-id-common公共模块包含了账户体系服务端的核心权限、token管理,内置在每个uniCloud服务空间里。
雪洛's avatar
雪洛 已提交
87

88
如开发者需要在自己的云函数/云对象里校验前端用户token,则需要引用uni-id-common公共模块。
雪洛's avatar
雪洛 已提交
89

90
uniCloud众多功能(如`DB Schema`的权限、uni-id-co)也都依赖 uni-id-common。
雪洛's avatar
雪洛 已提交
91

92
[详见](uni-id-common.md)
雪洛's avatar
雪洛 已提交
93

94
3. 云端[uni-config-center](uni-config-center.md)下的uni-id配置
雪洛's avatar
雪洛 已提交
95

96
`uni-id`在云端有很多配置,比如密码加密秘钥、短信和微信登录的appsecret等等。在`uni-config-center`下的`uni-id`目录下的config.json里存放着这些配置。
雪洛's avatar
雪洛 已提交
97

98
[见下](#config)
雪洛's avatar
雪洛 已提交
99

100 101
4. 客户端API
uni-app框架内置了uni-id的token管理。
雪洛's avatar
雪洛 已提交
102

103
uni-app与uniCloud搭配且使用uni-id,登录后自动下发token、网络传输层自动传输token(uni-app 2.7.13+版本)、token临近过期会自动续期(uni-app 3.4.13 +版本),也就是说开发者无需自己管理token了。
雪洛's avatar
雪洛 已提交
104

105
uni-app客户端还有一批uni-id相关的内置API:
106 107 108
- uniIDHasRole:判断当前用户是否拥有某角色。[详情](/api/global.md#uniidhasrole)
- uniIDHasPermission:判断当前用户是否拥有某权限。[详情](/api/global.md#uniidhaspermission)
- uniCloud.getCurrentUserInfo():客户端获取当前用户信息。[详情](client-sdk.md#client-getcurrentuserinfo)
雪洛's avatar
雪洛 已提交
109

110
5. 云端一体页面模板 [uni-id-pages](uniCloud/uni-id-pages)(含uni-id-co)
雪洛's avatar
雪洛 已提交
111

112
基于uni-id-common,DCloud还提供了一组完整的前端页面和后端[云对象](cloud-obj.md) ,合称`uni-id-pages`
雪洛's avatar
雪洛 已提交
113

114
uni-id-pages的功能包括:用户注册(含用户协议、隐私协议)、退出、修改密码、忘记密码等各种功能,同时适配PC宽屏和各种手机平台(App、H5、小程序)。
雪洛's avatar
雪洛 已提交
115

116
此外,DCloud的其他产品也为uni-id提供了众多支持:
117
- [uni-admin后台管理框架](admin.md),为uni-id提供了现成的用户、角色、权限的后台管理功能,以及注册用户统计报表。
雪洛's avatar
雪洛 已提交
118

119 120 121 122 123 124 125 126
以上全部是开源的。

**历史遗留**

在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公共模块还包含了忘记密码的代码,很浪费资源。

127
在云对象发布之前,DCloud基于云函数方式提供了uni-id-cf。但在HBuilderX 3.5 以后,推荐使用基于云对象的[uni-id-pages](uni-id-pages.md),代码更简单清晰。
128

129
从HBuilder 3.5起,[uni-id](https://ext.dcloud.net.cn/plugin?id=2116)和uni-id-cf都将被淘汰,不再更新。老的公共模块uni-id被拆开,变成了[uni-id-common](uni-id-common.md)公共模块和uni-id-co云对象。
130 131 132 133 134

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

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

135
老版升级指南,[详见](uni-id-pages.md#m-to-co)
雪洛's avatar
雪洛 已提交
136 137 138



雪洛's avatar
雪洛 已提交
139
## 快速上手@start
雪洛's avatar
雪洛 已提交
140

141
uni-id-common的插件市场地址为:[uni-id-common插件](https://ext.dcloud.net.cn/plugin?id=8576)。但一般不需要单独下载这个插件,但更新uni-id-common公共模块时需要从这里下载更新。
雪洛's avatar
雪洛 已提交
142 143 144

一般推荐直接使用uni-starter项目模板来开始开发,或者在新项目里导入uni-id-pages页面模板来使用。

145
uni-id云端的配置是依赖[uni-config-center](uni-config-center.md)公用模块的,在工程目录uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json。(如未安装uni-config-center需安装,如缺少目录需手动创建)
雪洛's avatar
雪洛 已提交
146 147 148 149 150 151 152 153 154 155 156

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. 确定注册登录方式

你的应用采用什么方式注册登录?比如用户名密码、手机号+短信验证码、或者微信登录。

157
很多登录方式涉及三方服务,需要开通[短信验证码服务](send-sms.md)、开通[App一键登录](univerify.md)、或者向微信等申请登录的appid和appsecret信息。
雪洛's avatar
雪洛 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170

申请开通相关服务后,需要把配置信息填写在云端配置文件config.json中。

2. 配置各项Secret

账户如果涉及密码,那么需要配置`passwordSecret`,账户的密码会根据`passwordSecret`使用sha1摘要加密算法以不可逆的方式存储在数据库中。

配置`tokenSecret`是为了防止token被第三方解密,模拟用户身份。

千万不要使用默认的passwordSecret和tokenSecret,会造成系统安全隐患。

云端的config.json还有各种配置,详见下个章节。前端的配置请参考uni-id-pages的文档。

雪洛's avatar
雪洛 已提交
171
## 云端配置config.json的说明@config
雪洛's avatar
雪洛 已提交
172

雪洛's avatar
雪洛 已提交
173
uni-id的云端配置文件在`uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json`中。
雪洛's avatar
雪洛 已提交
174 175 176 177 178 179 180 181 182 183

注意:

- **config.json是一个标准json文件,不支持注释**

配置项:

+ `passwordSecret`为用于加密密码入库的密钥
+ `tokenSecret`为生成token需要的密钥
+ `tokenExpiresIn`token有效期,以秒为单位
雪洛's avatar
雪洛 已提交
184 185
+ `passwordErrorLimit`密码错误重试次数,分ip记录密码错误次数,达到重试次数之后等待`passwordErrorRetryTime`时间之后才可以重试
+ `passwordErrorRetryTime`单位为秒
D
DCloud_LXH 已提交
186
+ 如果使用`sendSmsCode`接口发送短信需要前往[https://dev.dcloud.net.cn/#/pages/sms/base](https://dev.dcloud.net.cn/#/pages/sms/base)充值短信额度,配置`config.json``service`字段,字段说明见下方示例
雪洛's avatar
雪洛 已提交
187 188 189 190 191 192 193 194 195 196
+ 另外可以按照客户端平台进行不同的配置,参考下面示例

**下面的配置文件中所有时间的单位都是秒**

> !!!重要!!! passwordSecret与tokenSecret十分重要,切记妥善保存(不要直接使用下面示例中的passwordSecret与tokenSecret)。修改passwordSecret会导致老用户使用密码无法登录,修改tokenSecret会导致所有已经下发的token失效。如果重新导入uni-id切勿直接覆盖config.json相关配置

```json
// 如果拷贝此内容切记去除注释
{
  "passwordSecret": "passwordSecret-demo", // 数据库中password字段是加密存储的,这里的passwordSecret即为加密密码所用的密钥,注意修改为自己的密钥,使用一个较长的字符串即可
雪洛's avatar
雪洛 已提交
197
  "passwordStrength": "strong", // 密码强度,新增于 uni-id-pages 1.0.8版本,见下方说明
雪洛's avatar
雪洛 已提交
198 199
  "tokenSecret": "tokenSecret-demo", // 生成token所用的密钥,注意修改为自己的,使用一个较长的字符串即可
  "tokenExpiresIn": 7200, // 全平台token过期时间,未指定过期时间的平台会使用此值
雪洛's avatar
雪洛 已提交
200 201 202
  "tokenExpiresThreshold": 600, // 新增于uni-id 1.1.7版本,checkToken时如果token有效期小于此值且在有效期内则自动获取新token,请注意将新token返回给前端保存(云对象会自动保存符合uniCloud响应体规范的响应内的新token),如果不配置此参数则不开启自动获取新token功能
  "passwordErrorLimit": 6, // 密码错误最大重试次数
  "passwordErrorRetryTime": 3600, // 密码错误重试次数超限之后的冻结时间
雪洛's avatar
雪洛 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
  "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
雪洛's avatar
雪洛 已提交
224 225 226 227 228 229 230 231 232 233 234
    "tokenExpiresIn": 259200,
    "oauth": {
      "weixin-h5": { // 微信公众号登录配置
        "appid": "weixin appid",
        "appsecret": "weixin appsecret"
      },
      "weixin-web": { // 微信PC页面扫码登录配置
        "appid": "weixin appid",
        "appsecret": "weixin appsecret"
      }
    }
雪洛's avatar
雪洛 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
  },
  "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化,此项必须配置
D
DCloud_LXH 已提交
282
      "apiKey": "your apiKey", // apiKey  apiSecret 在开发者中心获取,开发者中心:https://dev.dcloud.net.cn/#/pages/uniLogin/index,文档:https://ask.dcloud.net.cn/article/37965
雪洛's avatar
雪洛 已提交
283 284 285 286 287 288
      "apiSecret": "your apiSecret"
    }
  }
}
```

雪洛's avatar
雪洛 已提交
289
### token自动刷新@auto-refresh-token
雪洛's avatar
雪洛 已提交
290 291 292 293 294 295 296

tokenExpiresThreshold用于指定token还有多长时间过期时自动刷新token。

例:指定`tokenExpiresThreshold:600,tokenExpiresIn:7200`,token过期时间为2小时,在token有效期不足10分钟时自动刷新token

在token还有5分钟过期时调用checkToken接口会返回新的token和新的token的过期时间(新token有效时间也是2小时)。

雪洛's avatar
雪洛 已提交
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
### 密码强度@password-strength

> 新增于uni-id-pages 1.0.8

支持以下四种内置规则

```js
{
  // 密码必须包含大小写字母、数字和特殊符号
  super: /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/,
  // 密码必须包含字母、数字和特殊符号
  strong: /^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/,
  // 密码必须为字母、数字和特殊符号任意两种的组合
  medium: /^(?![0-9]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]+$)[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/,
  // 密码必须包含字母和数字
  weak: /^(?=.*[0-9])(?=.*[a-zA-Z])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{6,16}$/
}
```

uni-id-co 与 uni-id-pages 内的前端页面均支持这四个内置规则,如需自定义规则,请参考:[uni-id-co自定义校验规则](uni-id-pages.md#custom-validator)

雪洛's avatar
雪洛 已提交
318
## token令牌
雪洛's avatar
雪洛 已提交
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339

首先解释下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里配置。

雪洛's avatar
雪洛 已提交
340
## 用户角色权限@rbac
雪洛's avatar
雪洛 已提交
341 342

为什么需要角色权限管理?
343 344
- 企业管理系统,比如[uni-admin](admin.md),除了超级管理员,不同账号通常需根据职位、责任设定不同的系统权限。比如部门管理员、Hr。
- [clientDB](clientdb.md)允许前端直接操作数据库,但部分字段应该是系统计算或管理员设置的,比如文章的阅读数、收藏数及是否加精置顶,这些字段不允许普通用户在前端通过clientDB直接修改,此时也需要通过权限控制来保证系统的安全稳定。 
雪洛's avatar
雪洛 已提交
345 346 347

`uni-id`基于经典的RBAC模型实现了角色权限系统。

雪洛's avatar
雪洛 已提交
348
### RBAC模型简介
雪洛's avatar
雪洛 已提交
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364

RBAC:Role-Based Access Control,基于角色的访问控制。

其基本思想:对系统操作的各种权限不是直接授予具体的用户,而是在用户集合与权限集合之间建立一个角色集合。每一种角色对应一组相应的权限。一旦用户被分配了适当的角色后,该用户就拥有此角色的所有权限。

![](https://bjetxgzv.cdn.bspapp.com/VKCEYUGU-dc-site/431878b0-0ca0-11eb-8a36-ebb87efcf8c0.png)

这样做的好处是,增强系统管理的扩展性,对于批量用户的权限变更,仅需变更该批用户角色对应权限即可,而无需对该批每个用户变更权限。

这个模型有三个关键名词:用户、角色、权限:
- 用户:使用系统的人,一个用户可以同时有多个角色,比如内容审核员、比如部门管理员
- 角色:权限的集合,一个角色可以有多个权限。比如内容审核员这个角色,有隐藏帖子权限、有帖子加精权限;而部门管理员这个角色,有给本部门新增员工权限、有删除本部门员工的权限。
- 权限:数据权限或业务权限,例如:删除用户、帖子加精等

用户、角色、权限都存在数据库了,都可以动态创建和修改。当权限对应的代码实现完成后,用户的新入、退出、角色升迁都无需再修改代码,在uni-admin后台的web界面可以由运维人员可视化的给每个用户调整角色、给每个角色调整权限。

雪洛's avatar
雪洛 已提交
365
### 用户
雪洛's avatar
雪洛 已提交
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386

用户信息存储在`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在跨表查询时的性能开销

雪洛's avatar
雪洛 已提交
387
### 角色
雪洛's avatar
雪洛 已提交
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447

角色信息存储在`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:出厂时可内置常用角色,也可上线后由运营人员动态创建角色。

雪洛's avatar
雪洛 已提交
448
### 权限
雪洛's avatar
雪洛 已提交
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

权限信息在`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中的权限配置和调整。

雪洛's avatar
雪洛 已提交
494
### 其他说明
雪洛's avatar
雪洛 已提交
495

496
uni-id将用户的角色权限缓存在token内。详情参考:[缓存角色权限](#cache-permission-in-token)
雪洛's avatar
雪洛 已提交
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

如下是通过token判断权限的简单示例:

```js
// 简单的权限校验示例
function hasPermission(token, permission) {
  const {
	  uid,
	  role,
	  permission
  } = await uniID.checkToken(token)
  return role.includes('admin') || checkTokenRes.permission.includes(permission) // admin用户的permission为空数组,但是拥有所有权限
}
```

512
注意:**在uniCloud admin中,封装了可视化的用户、权限、角色的管理,新增删除修改均支持。**无需自己维护。[详见](admin.md#mutiladmin)
雪洛's avatar
雪洛 已提交
513

514
## uni-id数据表@db-schema
雪洛's avatar
雪洛 已提交
515

516
`uni-id`的所有数据表,都在[opendb](opendb.md)规范中。
雪洛's avatar
雪洛 已提交
517

518
在unicloud [web控制台](https://unicloud.dcloud.net.cn/) 新建数据表时,可以从`uni-id`的模板分类里找到下面的表,并一键创建这些表。HBuilderX 3.4.11起新建 DB Schema 也有模板可选择。
雪洛's avatar
雪洛 已提交
519

雪洛's avatar
雪洛 已提交
520
### 用户表@user-table
雪洛's avatar
雪洛 已提交
521

522 523
存放用户基本信息。

雪洛's avatar
雪洛 已提交
524 525 526 527 528 529
表名:`uni-id-users`

| 字段				| 类型		| 必填	| 描述														|
| ----------------	| ---------	| ----	| -------------------------------------------				|
| \_id				| Object ID	| 是	| 存储文档 ID(用户 ID),系统自动生成						|
| username			| String	| 否	| 用户名,不允许重复										|
530
| password			| password	| 否	| 密码。加密存储											|
雪洛's avatar
雪洛 已提交
531
| nickname			| String	| 否	| 用户昵称													|
532
| gender			| int	| 否	| 用户性别:0 未知 1 男性 2 女性							|
雪洛's avatar
雪洛 已提交
533
| role				| Array		| 否	| 用户角色列表,由role_id组成的数组							|
534 535
| status			| int	| 是	| 用户状态:0 正常,1 禁用,2 审核中,3 审核拒绝,4 已注销	|
| dcloud_appid		| Array		| 否	| 允许登录的客户端的appid列表,不同应用同时复用一个user表时适用,比如	司机端和乘客端是2个appid,在登陆时可以隔离,[见下](#isolate-user)|
雪洛's avatar
雪洛 已提交
536
| mobile			| String	| 否	| 手机号码													|
537
| mobile_confirmed	| int	| 否	| 手机号验证状态:0 未验证 1 已验证,未验证用户不可登录		|
雪洛's avatar
雪洛 已提交
538
| email				| String	| 否	| 邮箱地址													|
539
| email_confirmed	| int	| 否	| 邮箱验证状态:0 未验证 1 已验证,未验证用户不可登录		|
雪洛's avatar
雪洛 已提交
540 541
| avatar			| String	| 否	| 头像地址													|
| wx_unionid		| String	| 否	| 微信unionid												|
542
| wx_openid			| Object	| 否	| 微信各个平台openid。子结构详见下文							|
雪洛's avatar
雪洛 已提交
543 544
| qq_unionid		| String	| 否	| QQ unionid												|
| qq_openid			| Object	| 否	| QQ各个平台openid。子结构详见下文							|
雪洛's avatar
雪洛 已提交
545 546 547
| ali_openid		| String	| 否	| 支付宝平台openid											|
| apple_openid		| String	| 否	| 苹果登录openid
| comment			| String	| 否	| 备注														|
548
| realname_auth		| Object	| 否	| 实名认证信息。子结构详见下文									|
雪洛's avatar
雪洛 已提交
549 550 551 552 553 554 555
| 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	| 否	| 用户自己的邀请码											|
556
| register_env		| Object	| 否	| 用户注册时的环境信息,新增于`uni-id 3.3.14`。子结构详见下文	|
雪洛's avatar
雪洛 已提交
557 558 559

**注意**

560
- 本表格只列出部分字段,完整字段[详见](https://gitee.com/dcloud/opendb/blob/master/collection/uni-id-users/collection.json)
雪洛's avatar
雪洛 已提交
561 562 563 564
- 最后登录时间、IP,并非只有登录操作会修改,token刷新时也会修改最后登录时间、ip。应用启动时若token有效则不会触发登录行为,也不会更新本值。

**wx_openid字段定义**

565
> opendb中uni-id-users表1.0.0调整为下面的结构,uni-id-co使用此标准。如何处理旧数据请参考:[自uni-id升级为uni-id-co+uni-id-common](uni-id-pages.md#m-to-co)
雪洛's avatar
雪洛 已提交
566 567 568 569 570

| 字段		| 类型	| 必填	| 描述					|
| -------	| ------| ----	| --------				|
| app		| String| 否	| app平台微信openid		|
| mp		| String| 否	| 微信小程序平台openid	|
雪洛's avatar
雪洛 已提交
571 572
| web		| String| 否	| 微信网页应用openid	|
| h5		| String| 否	| 微信公众号应用openid	|
雪洛's avatar
雪洛 已提交
573

雪洛's avatar
雪洛 已提交
574 575
**qq_openid字段定义**

576
> opendb中uni-id-users表1.0.0调整为下面的结构,uni-id-co使用此标准。如何处理旧数据请参考:[自uni-id升级为uni-id-co+uni-id-common](uni-id-pages.md#m-to-co)
雪洛's avatar
雪洛 已提交
577 578 579 580 581 582

| 字段		| 类型	| 必填	| 描述					|
| -------	| ------| ----	| --------				|
| app		| String| 否	| app平台QQ openid		|
| mp		| String| 否	| QQ小程序平台openid	|

雪洛's avatar
雪洛 已提交
583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
**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    | 否   | 联系人邮箱                                          |

**register_env字段定义**

603
**注意:该字段是在前端注册用户时记录的前端环境信息。如果是管理员在云端调用uni-id的addUser添加的用户则无此字段**
雪洛's avatar
雪洛 已提交
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618

| 字段			| 类型	| 必填	| 描述												|
|--				|--		|--		|--													|
|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									|


用户集合示例:

619
```json
雪洛's avatar
雪洛 已提交
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
{
  "_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"
  "last_login_ip": "123.120.11.128", //最后登录IP

}
```


雪洛's avatar
雪洛 已提交
636
#### 用户表索引使用注意@uni-id-users-indexes
雪洛's avatar
雪洛 已提交
637

638
目前 opendb 内提供的 uni-id-users表 包含完整的索引,数据库在索引量多且频繁更新的情况下可能会出现写入缓慢的情况,因此推荐开发者在使用 uni-id-users表 时可以删除没有用到的索引。
雪洛's avatar
雪洛 已提交
639

640
例:项目内只使用了微信登录,不使用其他登录方式,可以只保留`wx_unionid、wx_openid.mp`这些账号相关的索引,删除其他登录方式的索引(比如username、mobile)
雪洛's avatar
雪洛 已提交
641

642
不了解索引请参考:[索引](db-index.md)
雪洛's avatar
雪洛 已提交
643

雪洛's avatar
雪洛 已提交
644
### 验证码表
雪洛's avatar
雪洛 已提交
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663

表名:`opendb-verify-codes` 

该表的前缀不是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	| 是	| 验证码过期时间						|

雪洛's avatar
雪洛 已提交
664
### 角色表
雪洛's avatar
雪洛 已提交
665 666 667 668 669 670 671 672 673 674 675 676

表名:`uni-id-roles`

| 字段				| 类型			| 必填| 描述																	|
| ----------	| ---------	| ----| --------------------------------------|
| \_id				| Object ID	| 是	| 系统自动生成的Id											|
| role_id			| String		| 是	| 角色唯一标识													|
| role_name		| String		| 否	| 角色名,展示用												|
| permission	| Array			| 是	| 角色拥有的权限列表										|
| comment			| String		| 否	| 备注																	|
| created_date| Timestamp	| 是	| 角色创建时间													|

雪洛's avatar
雪洛 已提交
677
### 权限表
雪洛's avatar
雪洛 已提交
678 679 680 681 682 683 684 685 686 687 688

表名:`uni-id-permissions`

| 字段						| 类型			| 必填| 描述																	|
| ----------			| ---------	| ----| --------------------------------------|
| \_id						| Object ID	| 是	| 系统自动生成的Id											|
| permission_id		| String		| 是	| 权限唯一标识													|
| permission_name	| String		| 否	| 权限名,展示用												|
| comment					| String		| 否	| 备注																	|
| created_date		| Timestamp	| 是	| 权限创建时间													|

雪洛's avatar
雪洛 已提交
689
### 更多表
雪洛's avatar
雪洛 已提交
690 691 692

还有更多uni-id的配套数据表,可以在uniCloud [web控制台](https://unicloud.dcloud.net.cn/)新建表时选择相应模板。此处不再详述,仅罗列清单:

693
- 日志表:uni-id-log
雪洛's avatar
雪洛 已提交
694 695 696 697 698 699 700 701
- 积分表:uni-id-scores
- 地址信息表:uni-id-address
- 订单表:uni-id-base-order
- 设备表:uni-id-device
- 关注粉丝表:uni-id-followers
- 任务表:uni-id-task
- 任务日志表:uni-id-task-log

雪洛's avatar
雪洛 已提交
702
## 常量@constants
雪洛's avatar
雪洛 已提交
703

雪洛's avatar
雪洛 已提交
704
### 用户状态@user-status
雪洛's avatar
雪洛 已提交
705 706 707 708 709 710 711 712 713 714 715

对应`uni-id-users`表的status字段

|值	|说明		|
|--	|--			|
|0	|正常状态	|
|1	|封禁状态	|
|2	|审核中		|
|3	|审核失败	|
|4	|注销状态	|

雪洛's avatar
雪洛 已提交
716
### log类型@log-type
雪洛's avatar
雪洛 已提交
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731

对应`uni-id-log`表的type字段

|值			|说明			|
|--			|--				|
|logout		|登出			|
|login		|登录			|
|register	|注册			|
|reset-pwd	|重置密码		|
|bind-mobile|绑定手机		|
|bind-weixin|绑定微信账号	|
|bind-qq	|绑定QQ账号		|
|bind-apple	|绑定苹果账号	|
|bind-alipay|绑定支付宝账号	|

雪洛's avatar
雪洛 已提交
732
### 手机、邮箱验证码使用场景@sms-type
雪洛's avatar
雪洛 已提交
733 734 735 736 737 738 739 740 741

对应`opendb-verify-codes`表短信、邮箱验证码相关记录的的scene字段

|值					|说明					|
|--					|--						|
|login-by-sms		|短信验证码登录			|
|reset-pwd-by-sms	|短信验证码重置密码		|
|bind-mobile-by-sms |短信验证码绑定手机号	|

雪洛's avatar
雪洛 已提交
742
### 图形验证码使用场景@captcha-scene
雪洛's avatar
雪洛 已提交
743 744 745 746 747 748 749 750 751 752 753

对应`opendb-verify-codes`表图形验证码相关记录的scene字段

|值					|说明						|
|--					|--							|
|login-by-pwd		|用户名/手机/邮箱+密码登录	|
|login-by-sms		|短信验证码登录				|
|reset-pwd-by-sms	|短信验证码重置密码			|
|send-sms-code		|发送短信验证码				|
|bind-mobile-by-sms	|短信验证码绑定手机号		|

雪洛's avatar
雪洛 已提交
754
## uniIdRouter自动路由@uni-id-router
雪洛's avatar
雪洛 已提交
755 756 757

> 新增于 HBuilderX 3.5.0

758 759 760 761 762
uniIdRouter 是一个运行在前端的、对前端页面访问权限路由进行控制的方案。

大多数应用,都会指定某些页面需要登录才能访问。以往开发者需要写不少代码。

现在,只需在项目的`pages.json`内配置登录页路径、需要登录才能访问的页面等信息,uni-app框架的路由跳转,会自动在需要登录且客户端登录状态过期或未登录时跳转到登录页面。
雪洛's avatar
雪洛 已提交
763

雪洛's avatar
雪洛 已提交
764
结合以下代码及注释了解如何使用`uniIdRouter`
雪洛's avatar
雪洛 已提交
765 766 767 768 769 770 771 772 773

```json
{
	"pages": [
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "uni-app"
			},
雪洛's avatar
雪洛 已提交
774
			"needLogin": false // 当前页面是否需要登录才可以访问,此配置优先级高于uniIdRouter下的needLogin
雪洛's avatar
雪洛 已提交
775 776 777 778 779
		}, {
			"path": "pages/list/list",
			"style": {
				"navigationBarTitleText": "uni-app"
			},
780
			"needLogin": true
雪洛's avatar
雪洛 已提交
781 782 783 784 785 786 787 788 789 790 791 792 793
		}, {
			"path": "pages/detail/detail",
			"style": {
				"navigationBarTitleText": "uni-app"
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "black",
		"navigationBarTitleText": "uni-app",
		"navigationBarBackgroundColor": "#F8F8F8",
		"backgroundColor": "#F8F8F8"
	},
雪洛's avatar
雪洛 已提交
794
	"uniIdRouter": {
雪洛's avatar
雪洛 已提交
795 796 797 798 799 800 801 802 803 804
		"loginPage": "pages/index/index", // 登录页面路径
		"needLogin": [
			"pages/detail/.*" // 需要登录才可访问的页面列表,可以使用正则语法
		],
		"resToLogin": true // 自动解析云对象及clientDB的错误码,如果是客户端token不正确或token过期则自动跳转配置的登录页面,配置为false则关闭此行为,默认true
	}
}

```

805 806
以上代码,指定了登录页为首页`index`,然后将`list`页面和`detail`目录下的所有页面,设为需要登录才能访问。那么访问`list`页面和`detail`目录下的页面时,如果客户端未登录或登录状态过期(也就是uni-id-token失效),那么会自动跳转到`index`页面来登录。

雪洛's avatar
雪洛 已提交
807 808 809 810 811 812
与此功能对应的有两个uniCloud客户端api,`uniCloud.onNeedLogin()``uniCloud.offNeedLogin()`,开发者在监听onNeedLogin事件后,框架就不再自动跳转到登录页面,而是由开发者在onNeedLogin事件内自行处理。详情参考:[uniCloud.onNeedLogin](uniCloud/client-sdk.md?id=on-need-login)

自动跳转到登录页面时会携带uniIdRedirectUrl参数,其值为`encodeURIComponent(${跳转前的页面(包含路径和参数的完整页面地址)})`,如果希望用户登录后跳转回之前的页面,可以使用此参数实现。

以下为登录页面跳转到之前访问页面的简单示例:

813
pages/login/login.vue
雪洛's avatar
雪洛 已提交
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
```html
<template>
	<view>
		<button @click="login">login</button>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				uniIdRedirectUrl: ''
			}
		},
		onLoad(options) {
			this.uniIdRedirectUrl = decodeURIComponent(options.uniIdRedirectUrl)
		},
		methods: {
			async login() {
833
				// ...执行登录操作,在成功回调里跳转页面
雪洛's avatar
雪洛 已提交
834 835 836 837 838 839 840 841 842
				if (this.uniIdRedirectUrl) {
					uni.redirectTo({
						url: this.uniIdRedirectUrl
					})
				}
			}
		}
	}
</script>
843

雪洛's avatar
雪洛 已提交
844 845
```

雪洛's avatar
雪洛 已提交
846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
### 云对象响应触发needLogin

云对象抛出uni-id token过期或token无效错误码时,会触发客户端自动跳转配置的登录页面,以下代码为一个简单示例

```js
// todo云对象
const uniIdCommon = require('uni-id-common')
module.exports = {
	_before(){
		this.uniIdCommon = uniIdCommon.createInstance({
			clientInfo: this.getClientInfo()
		})
	},
	addTodo(title) {
		const {
			errCode,
			errMsg,
			uid
		} = await this.uniIdCommon.checkToken(this.getUniIdToken())
		if(errCode) { // uni-id-common的checkToken接口可能返回`uni-id-token-expired`、`uni-id-check-token-failed`错误码,二者均会触发客户端跳转登陆页面
			return {
				errCode,
				errMsg
			}
		}
		// ...
	}
}
```

876
客户端add-todo.vue
雪洛's avatar
雪洛 已提交
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
```html
<template>
	<!-- 略 -->
</template>
<script>
	export default {
		data() {
			return {
			}
		},
		onLoad() {},
		methods: {
			async addTodo(title){
				const todo = uniCloud.importObject('todo')
				await todo.addTodo(title) // 调用addTodo时云端checkToken如果返回了token错误、token失效的错误码就会自动跳转到配置的登录页面
			}
		}
	}
</script>
<style>
</style>
```

雪洛's avatar
雪洛 已提交
900 901
**注意**

雪洛's avatar
雪洛 已提交
902
- pages.json内有`uniIdRouter`节点上述逻辑才会生效,自HBuilderX 3.5.0起创建空项目模板会自动配置空的`uniIdRouter`节点
雪洛's avatar
雪洛 已提交
903
- uniIdRouter底层使用navigateTo、redirectTo、reLaunch、switchTab的拦截器进行页面跳转拦截,不会拦截进入首页,web端和app端会拦截原生tabbar点击,其他端不会拦截原生tabbar点击。
904
一般tabbar页面都不做自动跳转,而是在页面内再提供登录按钮。比如tabbar上有购物车或个人中心,点击购物车后在购物车页面内部会放一个提示语和按钮,告知用户需要登录。
905
在页面内判断用户是否登录,使用API[uniCloud.getCurrentUserInfo()](client-sdk.md#client-getcurrentuserinfo)
雪洛's avatar
雪洛 已提交
906

907
## 云端错误码@errcode
雪洛's avatar
雪洛 已提交
908

909
|错误码errCode							|错误信息errMsg							|说明													|
雪洛's avatar
雪洛 已提交
910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
|----									|----									|----													|
|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、手机号等)已被绑定	|-														|

雪洛's avatar
雪洛 已提交
942
## 多个应用复用相同uni-id-user表
雪洛's avatar
雪洛 已提交
943 944 945 946 947 948 949 950 951

有些系统由多个子应用组成,且没有各自独立服务空间,而是需要共享一个服务空间。此时就涉及一个问题,多个应用注册的账户都在uni-id-user表中,如何有效隔离。

比如一个打车软件,有乘客端、司机端、管理端,都要注册账户。它们也都有自己的DCloud appID(manifest.json里第一个配置)

uni-id-user表中有一个数组型字段`dcloud_appid`,可以存贮这个用户有权登陆哪个应用。

比如乘客端的appid是`__uni_111111`,司机端appid是`__uni_222222`,那么2个appid都存入`dcloud_appid`,即表示这个用户有权登录这2个应用。

雪洛's avatar
雪洛 已提交
952
### 隔离不同应用的用户@isolate-user
雪洛's avatar
雪洛 已提交
953 954 955

uni-id 3.3.0版本起用户注册时会自动在用户表的记录内标记为注册应用对应的用户,如果没有单独授权登录其他应用的话则只能登录这个应用。即在乘客端应用注册的,默认只能在乘客端应用登录。

956
如何授权登录其他应用请参考:[授权、禁止用户在特定客户端应用登录](#authorize-app)
雪洛's avatar
雪洛 已提交
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978

需要注意的是客户端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登录会被判定为不同的用户,如果此时数据库没有对应的记录,会报用户不存在的错误

雪洛's avatar
雪洛 已提交
979
### 隔离不同应用的配置@isolate-config
雪洛's avatar
雪洛 已提交
980

981 982
uni-id的config.json支持配置为数组,每项都是一个完整的配置,对不同的配置使用`dcloudAppid`字段进行区分(**此字段与项目内的manifest.json里面的DCloud AppId一致**),
uni-id会自动根据客户端的appid来判断该使用哪套配置。
雪洛's avatar
雪洛 已提交
983 984 985 986 987 988 989 990 991 992 993 994 995

需要注意的是客户端APPID信息是由端上传上来的,并非完全可信,尽量在入口处进行校验。例:

```js
exports.main = async function(event, context){
  if(context.APPID !== '__UNI__xxx1') {
    throw new Error('应用ID非法')
  }
}
```

**示例**

996
> 数组每一项都是一个完整的配置文件,全部选项请参考:[uni-id 配置](#config)
雪洛's avatar
雪洛 已提交
997 998 999

**注意:如果允许同一账号在不同端使用相同的账号+密码登录需要将不同端的passwordSecret设置成一样的**

1000
```json
雪洛's avatar
雪洛 已提交
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
[{
  "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
}]
```

雪洛's avatar
雪洛 已提交
1017
## 其他功能
雪洛's avatar
雪洛 已提交
1018

雪洛's avatar
雪洛 已提交
1019
### 裂变@fission
雪洛's avatar
雪洛 已提交
1020

雪洛's avatar
雪洛 已提交
1021
`config.json``uniCloud/cloudfuntions/common/uni-config-center/uni-id/config.json`,以下config.json均指此文件)内配置了`autoSetInviteCode: true`则在用户注册时会自动给设置不重复的6位邀请码
雪洛's avatar
雪洛 已提交
1022 1023 1024 1025 1026

`config.json`内配置了`forceInviteCode: true`则只有使用邀请码才可以注册。

`uni-id-co`在会产生注册行为的接口均添加了inviteCode参数,用于传递邀请码使注册用户接受邀请

雪洛's avatar
雪洛 已提交
1027
### 修改passwordSecret@modifysecret
雪洛's avatar
雪洛 已提交
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086

**注意:通常情况下设定好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只是进一步加强安全性。

雪洛's avatar
雪洛 已提交
1087
### 缓存角色权限@cache-permission-in-token
雪洛's avatar
雪洛 已提交
1088 1089 1090

使用`uni-id-common`时,token内会缓存用户的角色权限。

1091 1092 1093
为什么要在token缓存角色权限?token校验是高频操作,云数据库是按照读写次数来收取费用的,并且读写数据库会拖慢接口响应速度。

比较经济的做法就是在token里缓存角色权限。更好的方案是在redis里缓存角色权限,只是redis需要付费开通。
雪洛's avatar
雪洛 已提交
1094 1095 1096

**注意**

1097
- 由于角色权限缓存在token内,可能会存在权限已经更新但是用户token未过期之前依然是旧版角色权限的情况。可以调短一些token过期时间来减少这种情况的影响。或者使用redis来缓存用户权限
1098
- admin角色token内不包含permission,如需自行判断用户是否有某个权限,要注意admin角色需要额外判断一下,只要角色为admin或permission包含期待值,都视为拥有权限
雪洛's avatar
雪洛 已提交
1099

雪洛's avatar
雪洛 已提交
1100
### 自定义token内容@custom-token
雪洛's avatar
雪洛 已提交
1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127

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内的字段

雪洛's avatar
雪洛 已提交
1128
### 自定义国际化语言@custom-i8n
雪洛's avatar
雪洛 已提交
1129

雪洛's avatar
雪洛 已提交
1130 1131
完整词句列表参考:

雪洛's avatar
雪洛 已提交
1132
- [uni-id-co中文语言包](https://gitcode.net/dcloud/hello_uni-id-pages/-/blob/master/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/zh-hans.js)
雪洛's avatar
雪洛 已提交
1133
- [uni-id-common中文语言包](https://gitcode.net/dcloud/uni-id-common/-/blob/master/src/lang/zh-Hans.js)
雪洛's avatar
雪洛 已提交
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147

**用法**

`cloudfunctions/common/uni-config-center/uni-id/lang/`目录下创建`index.js`,内容示例如下:

```js
module.exports = {
	'zh-hant': { // 语言代码
		'alipay': '支付寶'
	}
}
```

uni-id会自动进行语言匹配,无需额外配置
雪洛's avatar
雪洛 已提交
1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227

### 自动保存用户sessionKey、accessToken等信息@save-user-token

uni-id-co在微信、QQ登录或注册时会自动保存用户的sessionKey、accessToken信息。

`uni-id-pages 1.0.8`之前,uni-id-co直接将这些信息保存在了用户表(uni-id-users)的third_party字段下,仅按照平台区分没有按照不同应用区分。具体结构如下

```js
{
  "_id": "uid",
  "wx_unionid": "xxx",
  "qq_unionid": "xxx",
  "third_party": {
    "mp_weixin": {
      "session_key": "xxxx"
    },
    "app_weixin": {
      "access_token": "accessToken",
      "access_token_expired": 1111
    },
    "mp_qq": {
      "session_key": "xxxx"
    },
    "app_qq": {
      "access_token": "accessToken",
      "access_token_expired": 1111
    }
  }
}
```

此结构无法满足多应用同一平台关联同一服务空间且允许用户跨应用登录的场景。因此在`uni-id-pages 1.0.8`及更高版本对此做出了调整,改为使用[uni-open-bridge-common](uni-open-bridge.md#uni-open-bridge-common)存储用户token信息。同时为了兼容旧版本上述third_party字段仍存有这些信息。

### 钩子@hooks

> 新增于 uni-id-pages 1.0.8

uni-id-co是一个完整的云对象,里面注册登录等流程都已完全实现,开发者不方便进行修改。例如要实现注册时为某端用户统一添加一个角色的功能,只能去修改uni-id-co的代码。因此uni-id-co提供了通过钩子干涉内置逻辑的功能
 
uni-id钩子函数需要在uni-config-center内配置。在`uni-config-center/uni-id`下创建hooks目录并在其内创建`index.js`内容如下

```js
module.exports = {
  beforeRegister: function (){
    // 注册前钩子
  }
}
```

#### beforeRegister@before-register

beforeRegister在注册用户记录入库前触发。钩子会接收到如下参数,需要返回处理后的用户记录用以入库存储

|参数名			|类型		|说明																																		|
|--					|--			|--																																			|
|userRecord	|Object	|即将入库的用户记录																											|
|clientInfo	|Object	|客户端信息,参考:[云对象 getClientInfo](cloud-obj.md#get-client-info)	|

以为__UNI_123123这个应用注册的用户添加"teacher"角色为例,beforeRegister钩子示例如下

```js
// 钩子函数示例 hooks/index.js

function beforeRegister({
  userRecord,
  clientInfo
} = {}) {
  if(clientInfo.appId === '__UNI_123123') {
    if(userRecord.role) {
      userRecord.role.push('teacher')
    } else {
      userRecord.role = ['teacher']
    }
  }
}

module.exports = {
  beforeRegister
}
```