11 KiB
IM 个人/企业注册登录前后端对接说明
1. 目标
当前客户端没有独立的企业码进入页面,登录页和注册页由用户选择“个人用户”或“企业用户”。前端提交登录和注册时必须显式带类型,后端不能再通过企业码是否为空来推断个人/企业。
核心规则:
- 前端新增字段
registration_type,类型为 int,取值只能是0或1。 registration_type=1时,后端按个人注册处理,内部租户使用default,数据写入default租户业务库。registration_type=0时,前端必须提交企业码,后端必须校验企业存在、启用且允许注册。- 登录新增字段
login_type,类型为 int,取值同样是0企业、1个人。 - 企业登录或企业注册都不允许使用
default。 default是系统保留的个人租户码,不允许作为企业码输入。
一句话规则:注册类型由 registration_type 决定,登录类型由 login_type 决定;个人落 default,企业必须校验企业码。
2. 前端注册字段
注册页提交发码和最终注册时都带同一组注册上下文字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
registration_type |
int | 是 | 0 表示企业注册,1 表示个人注册 |
tenant_code |
string | 企业注册必填 | 企业码;个人注册不填或传空字符串 |
前端规则:
registration_type |
tenant_code |
后端处理 |
|---|---|---|
1 |
空 | 允许提交,后端内部使用 default |
1 |
非空 | 后端拒绝,避免误入库 |
0 |
空 | 后端拒绝,提示请输入企业码 |
0 |
default、Default、DEFAULT |
后端拒绝 |
0 |
其他值 | 后端在注册链路中校验 |
用户在企业注册模式输入 default 时,建议提示:
default 是系统保留企业码。个人注册请选择个人用户,企业注册请输入企业提供的企业码。
3. 企业码校验
不新增单独的企业码校验接口,也不要求前端在注册前调用 /v1/im/tenants/resolve。
后端在注册发码和最终注册链路中校验:
-
registration_type=1- 不要求企业码。
- 即使请求 Header 或内部路由使用
default,也属于服务端内部租户,不是用户输入企业码。 - 创建用户时写入
default租户业务库。
-
registration_type=0tenant_code必填。tenant_code大小写不敏感等于default时拒绝。- 查询企业是否存在、启用、允许注册。
- 通过后在对应企业租户业务库创建用户。
企业不存在、停用或不允许注册时,客户端统一提示:
企业不存在或不可用
客户端不要区分不存在、停用、禁用注册等内部原因。
/v1/im/tenants/resolve 只保留给服务发现、品牌配置、历史企业入口等场景使用;当前注册页不依赖它完成主流程。
4. 前端登录字段
登录页提交 WebSocket {login} 时带登录上下文字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
login_type |
int | basic 登录必填 | 0 表示企业登录,1 表示个人登录 |
tenant_code |
string | 企业登录必填 | 企业码;个人登录不填、传空字符串或内部传 default |
登录规则:
login_type |
tenant_code / hi.tenant |
后端处理 |
|---|---|---|
1 |
default |
个人登录,允许 |
1 |
非 default |
拒绝,避免个人登录误进企业租户 |
0 |
空 | 拒绝,提示请输入企业码 |
0 |
default、Default、DEFAULT |
拒绝,统一提示“企业不存在或不可用” |
0 |
其他值 | 企业登录,后端按该租户认证 |
企业用户如果在企业入口输入 default,后端返回策略错误,错误参数为 what=tenant_code、reason=TENANT_CODE_FORBIDDEN;客户端统一展示:
企业不存在或不可用
个人登录 {login} 示例:
{
"login": {
"id": "login-1",
"scheme": "basic",
"login_type": 1,
"tenant_code": "",
"secret": "base64(account:password)"
}
}
企业登录 {login} 示例:
{
"login": {
"id": "login-1",
"scheme": "basic",
"login_type": 0,
"tenant_code": "acme",
"secret": "base64(account:password)"
}
}
5. 个人注册流程
个人注册必须显式提交 registration_type=1。
- 用户选择个人注册。
- 前端提交
registration_type=1,tenant_code为空。 - 注册发码、验证码校验、WebSocket
hi、acc注册链路使用服务端内部租户default。 - 服务端将用户创建到
default租户对应的业务库。
发码示例:
POST /v1/im/auth/verification-codes
X-MeetingIM-APIKey: <api_key>
X-IM-Schema: 1
X-Tenant-Code: default
X-Request-ID: <uuid>
{
"scene": "register",
"registration_type": 1,
"tenant_code": "",
"phone": "19122421333"
}
WebSocket 握手示例:
{
"hi": {
"id": "hi-1",
"ver": "0.25",
"ua": "ios/1.0.0",
"platf": "ios",
"lang": "zh-CN",
"tenant": "default"
}
}
最终注册 {acc} 示例:
{
"acc": {
"id": "acc-1",
"user": "new",
"scheme": "basic",
"registration_type": 1,
"tenant_code": "",
"username": "henry",
"phone": "19122421333",
"secret": "base64(henry:password)",
"login": true,
"cred": [
{
"meth": "tel",
"val": "19122421333",
"resp": "verification_token",
"params": {"challenge_id": "challenge-id"}
}
]
}
}
6. 企业注册流程
企业注册必须显式提交 registration_type=0。
- 用户选择企业注册。
- 用户填写企业码,例如
acme。 - 前端直接提交用户输入的企业码,不做企业码存在性或保留字拦截。
- 获取验证码、提交注册时都带
registration_type=0和tenant_code=acme。 - 后端在注册链路中校验企业是否存在且可用。
- 校验通过后,服务端在该企业租户业务库中创建用户;校验不通过时返回“企业不存在或不可用”。
注册发码请求:
POST /v1/im/auth/verification-codes
X-MeetingIM-APIKey: <api_key>
X-IM-Schema: 1
X-Tenant-Code: acme
X-Request-ID: <uuid>
{
"scene": "register",
"registration_type": 0,
"tenant_code": "acme",
"phone": "19122421333"
}
WebSocket hi:
{
"hi": {
"id": "hi-1",
"ver": "0.25",
"ua": "android/1.0.0",
"platf": "android",
"lang": "zh-CN",
"tenant": "acme"
}
}
最终注册 {acc} 示例:
{
"acc": {
"id": "acc-1",
"user": "new",
"scheme": "basic",
"registration_type": 0,
"tenant_code": "acme",
"username": "henry",
"phone": "19122421333",
"secret": "base64(henry:password)",
"login": true,
"cred": [
{
"meth": "tel",
"val": "19122421333",
"resp": "verification_token",
"params": {"challenge_id": "challenge-id"}
}
]
}
}
7. 后端校验规则
后端负责注册链路的最终校验,前端不做企业码拦截。
-
registration_type缺失或不是0|1- 拒绝注册。
- 返回
MALFORMED_REQUEST,提示客户端升级或检查参数。
-
registration_type=1- 不从空企业码推断,而是由该字段明确进入个人注册。
- 内部租户码设置为
default。 - 拒绝用户提交的非空
tenant_code,避免误入库。 - 注册时写入
default租户业务库。
-
registration_type=0tenant_code必填。- 先 trim 首尾空格。
- 如果大小写不敏感等于
default,拒绝注册。 - 否则查询租户是否存在、是否启用、是否允许注册。
- 校验通过后绑定对应租户。
建议错误:
{
"code": 30000,
"reason": "MALFORMED_REQUEST",
"message": "invalid registration_type or tenant_code"
}
企业不可用时,客户端展示:
企业不存在或不可用
8. 缓存和本地数据隔离
客户端本地缓存、Token、会话、草稿、附件缓存必须继续按 tenant_code + user_id 隔离。
个人注册用户登录后,缓存键中的 tenant_code 使用 default。企业用户登录后,使用对应企业码。切换个人/企业身份时必须清理当前连接并重新 hi,不能在同一个 WebSocket 中切换租户。
9. 验收用例
| 场景 | 预期 |
|---|---|
login_type=1,hi.tenant=default |
个人登录成功进入 default 租户认证 |
login_type=1,hi.tenant=acme |
拒绝登录 |
login_type=0,企业码空 |
拒绝登录,提示请输入企业码 |
login_type=0,企业码为 default |
拒绝登录,提示企业不存在或不可用 |
login_type=0,企业码有效 |
在该企业租户认证 |
registration_type=1,企业码空,手机号注册 |
成功进入 default 租户注册链路 |
registration_type 缺失 |
拒绝注册,返回参数错误 |
registration_type=1,企业码非空 |
拒绝或忽略,建议拒绝,避免误入库 |
registration_type=0,企业码空 |
拒绝注册,提示请输入企业码 |
registration_type=0,企业码为 default |
拒绝注册 |
registration_type=0,企业码不存在 |
发码或注册失败,提示企业不存在或不可用 |
registration_type=0,企业码已停用 |
统一提示企业不存在或不可用 |
registration_type=0,企业码有效 |
注册写入该企业租户库 |
| 个人用户注册成功后登录 | tenant_code=default,可正常登录、收发消息 |
10. 对客户端的明确要求
- 登录页和注册页都必须明确区分个人和企业。
- basic 登录必须提交
login_type。 - 发码和最终注册都必须提交
registration_type。 - 个人登录/注册提交类型
1,不要让用户输入或看到default。 - 企业登录/注册提交类型
0和用户输入的企业码。 - 企业用户手动输入
default也直接提交,由后端返回错误,客户端按后端错误展示提示。 - 不需要为了注册主流程调用
/v1/im/tenants/resolve。
11. 对后端的明确要求
- basic 登录链路必须以
login_type作为个人/企业登录的判定来源。 - 注册链路必须以
registration_type作为个人/企业注册的判定来源。 - 不允许仅根据企业码是否为空推断个人注册或个人登录。
- 登录、注册、发码、验证码校验、WebSocket
hi/login/acc链路保持同一租户上下文。 - 显式企业码
default必须拒绝,防止用户绕过企业码校验。 - 企业登录/注册的企业码必须校验存在、启用、允许登录/注册。
- 日志中要能看出来源是
personal_default还是enterprise_code,方便排查误入库问题。