登录模块审核

This commit is contained in:
Shen Wei
2026-04-30 18:40:55 +08:00
parent 4030a91100
commit 57600598ac
34 changed files with 2544 additions and 2431 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -2,8 +2,8 @@
**状态**: Draft
**作者**: 产品经理
**最后更新**: 2026-04-25v1.4 §5.5 后端数据模型迁移至独立文档 `DATA_MODEL/DATA_MODEL_LOGIN.md`
**版本**: 1.4
**最后更新**: 2026-04-30v2.0 根据 review 后的 §4 用户故事全面同步 §5 功能详细说明:删除找回用户名流程及邮件模板;找回密码改为纯短信流程;新增 §5.5 手机验证码登录详细说明§6 技术注意事项更新短信依赖/风险/开放问题§8.2 接口清单同步正式功能状态
**版本**: 2.0
**所属系统**: Fonrey 房产经纪管理系统
**关联模块**: 组织人事管理、权限管理、系统管理
@@ -28,17 +28,17 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|------|--------|---------|
| 多租户环境下,客户端不知道应该连接哪个租户的服务端 | 新用户首次安装后无法正常使用 | 系统无法启动,用户体验极差 |
| 账号密码裸露登录,缺乏验证码保护 | 所有用户 | 存在暴力破解、自动化恶意登录风险 |
| 用户忘记账号或密码无自助找回通道 | 一线经纪人 | 依赖管理员手动重置,效率低 |
| 账号未与实名经纪人档案绑定 | 系统管理员、合规审计 | 操作行为无法追溯至自然人 |
| 用户忘记账号或密码无自助找回通道 | Agent经纪人 | 依赖管理员手动重置,效率低 |
| 账号未与实名经纪人档案绑定 | Tenant Admin租户管理员、合规审计 | 操作行为无法追溯至自然人 |
| 无多因素认证,安全系数低 | 管理层、数据合规 | 存在账号冒用、数据泄露风险 |
### 1.3 目标用户
| 角色 | 描述 | 使用频率 |
|------|------|----------|
| 一线经纪人 | 每日登录系统使用房源/客源功能 | 每日高频 |
| Agent经纪人 | 每日登录系统使用房源/客源功能 | 每日高频 |
| 店长 / 经理 | 登录后查看全店数据、管理任务 | 每日 |
| 系统管理员 | 管理账号创建、密码重置、租户初始化 | 按需 |
| Tenant Admin租户管理员 | 管理账号创建、密码重置、租户初始化 | 按需 |
| 新安装用户 | 首次安装客户端后需完成 Tenant 识别 | 一次性 |
---
@@ -49,7 +49,7 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
|------|------|----------|--------|----------|
| 降低登录失败率 | 账号密码正确情况下的登录成功率 | 待统计 | ≥ 99% | 上线后 30 天 |
| 防止恶意登录 | 每日验证码拦截异常登录请求数 | 0无保护 | 建立基线同IP异常次数 > 5次/分钟触发封锁 | 上线后持续监控 |
| 提升找回账号效率 | 用户自助找回密码耗时 | 依赖管理员约1工作日 | < 3 分钟(邮件/短信自助) | 上线后 30 天 |
| 提升找回账号效率 | 用户自助找回密码耗时 | 依赖管理员约1工作日 | < 3 分钟(短信验证码自助) | 上线后 30 天 |
| 确保账号实名绑定率 | 拥有系统账号且未与员工档案绑定的账号比例 | 待统计 | 0%(强制绑定) | 上线即达标 |
| Tenant 识别成功率 | 首次安装后成功完成 Tenant 识别的用户比例 | 待统计 | ≥ 98% | 上线后 30 天 |
@@ -58,11 +58,12 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
## 3. 非目标(本期不做)
- **手机验证码登录**:移动端小程序上线后实现,本期**接口预留**UI 入口以「即将开放」禁用态展示
> **注意**:本条已更新。手机验证码登录已升级为 **MVP 正式功能**(见 Story 5与密码登录并列提供。此非目标条目保留仅作版本记录已失效。
- **微信扫码登录**:移动端小程序上线后实现,本期**接口预留**UI 入口以「即将开放」禁用态展示
- **单点登录SSO/ 企业微信集成**:后续版本规划
- **多设备并发登录的强制踢出策略**:本期允许同账号多端登录,后续安全策略模块规划
- **登录时段限制 / IP 白名单**:安全策略模块另行规划
- **管理后台Platform Admin登录**系统管理员登录管理后台的流程属于系统管理模块,本 PRD 专注租户内用户登录
- **管理后台Platform Admin登录**Tenant Admin租户管理员登录管理后台的流程属于系统管理模块,本 PRD 专注租户内用户登录
---
@@ -72,31 +73,33 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
### Story 1新用户首次启动客户端——Tenant 识别
**As** 新安装 Fonrey 客户端的经纪人,**I want** 在首次启动时输入所属公司的 Tenant ID 完成租户识别,**So that** 客户端能连接到正确的服务端,后续显示对应公司的登录界面和数据。
**As** 新安装 Fonrey 客户端的经纪人,**I want** 在首次启动时输入所属公司的 12位 Tenant Code 完成租户识别,**So that** 客户端能连接到正确的服务端,后续显示对应公司的登录界面和数据。
**验收标准**
- [ ] 客户端首次启动时(本地无 Tenant ID 缓存自动呈现「Tenant 识别」界面,而非直接显示登录界面
- [ ] 界面包含:产品 Logo、产品名称「Fonrey 房睿」、说明文案「请输入您公司的专属识别码」、Tenant ID 输入框、「确认」按钮
- [ ] Tenant ID 输入框支持粘贴操作,自动去除前后空格
- [ ] 客户端首次启动时(本地无 Tenant Code 缓存自动呈现「Tenant 识别」界面,而非直接显示登录界面
- [ ] 界面包含:产品 Logo、产品名称「Fonrey 房睿」、说明文案「请输入您公司的专属识别码」、Tenant Code 输入框、「确认」按钮
- [ ] Tenant Code 输入框支持粘贴操作,自动去除前后空格
- [ ] 点击「确认」后,客户端向服务端发起 Tenant 验证请求(`POST /api/auth/tenant/verify/`展示加载状态spinner
- [ ] **验证成功**服务端返回租户名称及品牌信息如公司名称、Logo URL客户端将 Tenant ID 写入本地持久化存储自动跳转至该租户的登录界面界面顶部展示「正在登录XX 房产」
- [ ] **验证失败Tenant ID 无效)**:输入框下方显示红色错误提示「识别码无效,请联系您的系统管理员获取正确的识别码」Tenant ID 不写入本地缓存;用户可重新输入
- [ ] **验证成功**服务端返回租户名称及品牌信息如公司名称、Logo URL客户端将 Tenant Code 写入本地持久化存储自动跳转至该租户的登录界面界面顶部展示「正在登录XX 房产」
- [ ] **验证失败Tenant Code 无效)**:输入框下方显示红色错误提示「识别码无效,请联系您的Tenant Admin租户管理员获取正确的识别码」Tenant Code 不写入本地缓存;用户可重新输入
- [ ] **网络异常**:显示「网络连接失败,请检查网络后重试」,提供「重试」按钮
- [ ] 非首次启动(本地已有合法 Tenant ID 缓存):直接跳过识别界面,进入登录界面
- [ ] 登录界面提供「切换公司」入口(链接文字,非主要 CTA点击后清除本地 Tenant ID 缓存并重新显示 Tenant 识别界面;确认前弹出二次确认「切换公司将退出当前账号,是否继续?」
- [ ] 非首次启动(本地已有合法 Tenant Code 缓存):直接跳过识别界面,进入登录界面
- [ ] 登录界面提供「切换公司」入口(链接文字,非主要 CTA点击后清除本地 Tenant Code 缓存并重新显示 Tenant 识别界面;确认前弹出二次确认「切换公司将退出当前账号,是否继续?」
- [ ] Tenant 验证接口属于公开接口,无需鉴权;但需对单 IP 请求频率限制(每分钟 ≤ 10 次)以防止枚举攻击
---
### Story 2经纪人通过账号密码登录
### Story 2经纪人通过手机号和密码登录
**As** 已识别租户的经纪人,**I want** 通过用户名和密码完成登录,**So that** 进入系统开始工作。
**As** 已识别租户的经纪人,**I want** 通过手机号和密码完成登录,**So that** 进入系统开始工作。
> **说明**普通员工的登录账号即为其手机号由Tenant Admin租户管理员在新增员工时自动创建无需记忆额外用户名。Tenant Admin 账号的登录名为平台运营自定义字符串,不受此约束。
**验收标准**
- [ ] 登录界面展示:租户品牌标识(公司 Logo + 公司名称)、用户名输入框、密码输入框、滑块拼图验证区域、「登录」按钮
- [ ] 用户名输入框 Placeholder「请输入用户名」;支持英文字母、数字、下划线,最大长度 50 字符
- [ ] 登录界面展示:租户品牌标识(公司 Logo + 公司名称)、手机号输入框、密码输入框、滑块拼图验证区域、「登录」按钮
- [ ] 手机号输入框 Placeholder「请输入您的手机号」;仅接受数字字符(非数字自动过滤),固定 11 位
- [ ] 密码输入框默认密文显示,右侧提供「显示/隐藏」图标切换明密文
- [ ] **行为验证码(滑块拼图)**:展示一张带缺口的背景图和一块可拖动的拼图碎片,用户通过拖动滑块将碎片移动至缺口位置完成验证;无需输入任何字符,操作直观快速
- [ ] 验证逻辑:前端记录滑动轨迹(坐标序列 + 耗时),与背景图缺口位置一同发送至服务端;服务端综合校验**位置偏差**(允许 ±5px 容差)和**轨迹特征**(是否存在人类滑动的加速/减速规律)以区分机器行为
@@ -104,92 +107,121 @@ Fonrey 是一套面向房产经纪公司的 B2B SaaS 平台,采用多租户架
- [ ] 验证成功后,拼图区域显示绿色对勾 + 「验证通过」文案,状态持续至本次登录提交完成
- [ ] 提供「刷新」图标按钮,允许用户主动刷新背景图(针对图片模糊或缺口不清晰的情况)
- [ ] 背景图从预置图库中随机抽取,缺口位置每次随机生成,防止固定模式被预测
- [ ] 三项(用户名、密码、验证码)均有填写后,「登录」按钮才可点击(否则置灰)
- [ ] 三项(手机号、密码、验证码)均有填写后,「登录」按钮才可点击(否则置灰)
- [ ] 点击「登录」触发前端格式校验:
- 用户名为空 → 输入框下方红色提示「请输入用户名
- 手机号为空 → 输入框下方红色提示「请输入手机号
- 手机号不满 11 位 → 提示「请输入完整的 11 位手机号」
- 密码为空 → 提示「请输入密码」
- 验证码为空 → 提示「请输入验证
- 验证码为空 → 提示「请完成滑块验证」
- [ ] 格式校验通过后,向服务端发起登录请求,按钮进入 loading 状态防止重复提交
- [ ] **登录成功**:服务端返回 Session Token客户端存储 Token跳转至系统首页;顶部显示欢迎信息「欢迎回来,{姓名}」
- [ ] **登录失败(用户名或密码错误)**:显示「用户名或密码错误,请重新输入」(不区分是用户名错误还是密码错误,防止枚举攻击);验证码自动刷新;密码输入框清空;用户名保留
- [ ] **登录成功(常规)**:服务端返回 Session Token`is_initial_password` 标记;客户端存储 Token
-`is_initial_password = False`:直接跳转系统首页,顶部显示欢迎信息「欢迎回来,{姓名}」
-`is_initial_password = True`**立即跳转「修改初始密码」强制页面**,不可关闭、不可跳过、不可访问任何其他功能页面(详见 §5.3.4
- [ ] **登录失败(手机号或密码错误)**:显示「手机号或密码错误,请重新输入」(不区分具体原因,防止枚举攻击);验证码自动刷新;密码输入框清空;手机号保留
- [ ] **登录失败(验证码错误)**:显示「验证码有误,请重新输入」;验证码自动刷新;验证码输入框清空
- [ ] **账号被锁定**(同一账号密码连续错误 ≥ 5 次):显示「账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁」;锁定状态下「登录」按钮置灰
- [ ] **账号已停用**:显示「账号已停用,请联系您的管理员」
- [ ] **Session 过期**:用户在系统内操作时 Session 过期,自动跳转至登录界面,并提示「登录已过期,请重新登录」
- [ ] 登录界面底部提供:「忘记用户名」链接、「忘记密码」链接(详见 Story 3、Story 4
- [ ] 登录界面底部提供:「忘记密码」链接(详见 Story 3移除「忘记用户名」入口普通员工用户名即手机号无需找回Tenant Admin 如忘记用户名请联系平台运营
---
### Story 3经纪人找回用户名
### Story 3经纪人找回密码
**As** 忘记用户名的经纪人,**I want** 通过绑定的邮箱或手机号找回用户名,**So that** 不依赖管理员也能自助恢复登录
**As** 忘记密码的经纪人,**I want** 通过手机号 + 短信验证码完成身份核验,重新设定密码,**So that** 无需邮箱、无需联系管理员,独立完成密码重置
> **说明**考虑到大多数Agent经纪人没有常用邮箱本期找回密码统一通过短信验证码实现废弃邮箱找回方式。账号中 `email` 字段在本系统无任何必须业务用途,完全可选。
**验收标准**
- [ ] 点击登录界面「忘记用户名」链接,跳转至「找回用户名」页面(或弹窗
- [ ] 找回方式(本期以邮箱为主,手机号为预留字段):
- 邮箱找回:输入注册邮箱,系统校验邮箱是否与已知账号匹配,匹配成功则发送包含用户名的邮件至该邮箱
- 手机号找回预留UI 入口以「即将开放」禁用态展示)
- [ ] 邮箱输入框:格式校验(包含「@」和域名),错误时提示「请输入有效的邮箱地址」
- [ ] 点击「发送」后:
- 邮箱存在且已绑定账号 → 显示「用户名已发送至您的邮箱,请查收」;发送按钮进入 60 秒倒计时不可重复点击
- 邮箱不存在 → **不提示「邮箱未注册」**(防止用户信息枚举),统一显示「如该邮箱已绑定账号,您将收到一封包含用户名的邮件」
- [ ] 邮件内容:纯文本邮件,包含用户名、发送时间,及「如非本人操作请联系管理员」说明
- [ ] 发送频率限制:同一邮箱 1 小时内最多发送 3 次
- [ ] 提供「返回登录」链接
- [ ] 点击登录界面「忘记密码」链接,跳转至「找回密码」流程Stepper 分步页面,共三步
**步骤一:输入手机号**
- [ ] 页面显示手机号输入框11 位数字,自动过滤非数字)、「获取验证码」按钮、「返回登录」链接
- [ ] 手机号为空或不足 11 位 → 点击「获取验证码」时在输入框下方提示「请输入完整的 11 位手机号」
- [ ] 手机号格式合法后,点击「获取验证码」,按钮进入 60 秒倒计时冷却态(「重新获取(59s)」),倒计时结束后按钮恢复可点击
- [ ] 服务端收到请求后:
- 若该手机号**存在**且账号状态为 `active`:向该号码发送 6 位数字短信验证码,有效期 **10 分钟**
- 若手机号**不存在**或账号已停用:页面统一提示「如该手机号已注册,验证码将在 1 分钟内发送」(**不泄露账号是否存在**
- [ ] 同一手机号 1 小时内最多发送 **5 次**短信验证码,超限后提示「发送次数过多,请 1 小时后再试」
- [ ] 短信内容模板「【Fonrey 房睿】您的密码重置验证码为 {code}10 分钟内有效,请勿泄露。」
**步骤二:输入短信验证码**
- [ ] 页面显示6 位验证码输入框(支持分格输入)、「重新发送」倒计时链接、「下一步」按钮
- [ ] 「下一步」按钮6 位验证码全部输入后方可点击
- [ ] 服务端校验验证码:
- 正确且未过期 → 进入步骤三,颁发一次性 `sms_reset_token`(有效期 15 分钟,一次性,服务端存储)
- 错误 → 提示「验证码有误,请重新输入」,错误次数 ≥ 5 次则本次验证码作废,需重新获取
- 已过期 → 提示「验证码已过期,请重新获取」
**步骤三:重置密码**
- [ ] 步骤三依赖步骤二颁发的 `sms_reset_token`(通过 URL 参数或会话状态传递Token 无效或过期 → 显示「操作已超时,请重新发起找回密码」,跳回步骤一
- [ ] **本页面复用「设置新密码」公共组件**(与首次登录强制修改密码页面为同一组件,详见 §5.3.4),保持 UI 与交互逻辑完全一致;入口上下文不同时,仅页面标题和提示文案有所差异:
| 元素 | 首次登录强制修改§5.3.4 | 找回密码步骤三(本 Story |
|------|--------------------------|--------------------------|
| 页面标题 | 「欢迎使用 Fonrey请先设置您的登录密码」 | 「重置您的登录密码」 |
| 提示文案 | 「您当前使用的是初始密码,为保障账号安全,请立即设置新密码后开始使用」 | 「请输入您的新密码,设置完成后请使用新密码重新登录」 |
| 提交按钮文案 | 「确认并进入系统」 | 「确认重置密码」 |
| 提交后跳转 | `is_initial_password = False`Session 保持,直接进入首页 | 所有 Session 立即失效,跳转登录界面并提示「密码已重置,请使用新密码登录」 |
- [ ] 提交成功后:`is_initial_password` 置为 **`False`**(找回密码属于用户主动操作,已完成身份核验,无需再触发强制修改流程)
> **注意**:与首次登录流程不同,找回密码时用户已通过短信验证码完成了身份核验,本次密码设置即视为"用户本人主动设置",不应再触发 `is_initial_password = True` 的二次强制修改。
---
### Story 4经纪人找回密码
### Story 4经纪人找回用户名(已废弃)
**As** 忘记密码的经纪人,**I want** 通过已知用户名 + 绑定邮箱(或手机号)自助重置密码,**So that** 不依赖管理员也能快速恢复登录
> **状态**已废弃。普通员工用户名固定为手机号无需找回Tenant Admin 如忘记用户名,请联系平台运营线下处理。本 Story 保留占位以维持版本记录,实现时跳过此 Story
---
### Story 5手机验证码登录MVP 实现)
**As** 已有账号的经纪人,**I want** 通过手机号 + 短信验证码直接登录,**So that** 在忘记密码或不想输入密码时,仍能快速进入系统。
> **说明**:短信基础设施(`sms_otp_records` 表、OTP 发送/校验逻辑)已在 Story 3 找回密码中建设完成,本 Story 直接复用,实现成本极低。登录界面提供「密码登录」和「验证码登录」两个并列入口,用户自由切换,两种方式均为 MVP 正式功能。
**验收标准**
- [ ] 点击登录界面「忘记密码」链接,跳转至「找回密码」流程(分步骤页面或 Stepper 组件)
- [ ] 登录界面提供两种登录方式的切换 Tab**「密码登录」**(默认选中)和 **「验证码登录」**
- [ ] 切换 Tab 时,输入区域平滑切换,已填内容清空,滑块验证状态重置
**步骤一:身份验证**
**「验证码登录」界面元素**
- [ ] 手机号输入框(规格同 Story 211 位数字,自动过滤非数字)
- [ ] 验证码输入框6 位数字分格输入)+ 「获取验证码」按钮60 秒倒计时冷却态)
- [ ] 滑块拼图验证区域(规格同 Story 2**先通过滑块验证,再允许点击「获取验证码」**
- [ ] 「登录」按钮(手机号 + 验证码均填写后方可点击)
- [ ] 用户输入:用户名 + 邮箱(本期);手机号找回为预留入口(禁用态)
- [ ] 服务端校验用户名与邮箱是否匹配,不泄露具体原因(统一提示「如信息匹配,重置链接将发送至您的邮箱」)
- [ ] 校验通过后,向绑定邮箱发送含一次性重置链接的邮件;链接有效期 **30 分钟**,使用后立即失效
- [ ] 同一账号 1 小时内最多发送 3 次重置邮件
**获取验证码逻辑**
- [ ] 用户须先完成滑块验证,「获取验证码」按钮方可点击;未完成滑块时点击 → 提示「请先完成滑块验证」
- [ ] 点击「获取验证码」后,服务端:
- 手机号格式不合法 → 前端拦截,提示「请输入完整的 11 位手机号」
- 手机号存在且状态 `active` → 发送 6 位 OTP有效期 **5 分钟**,存入 `sms_otp_records``scene = 'login'`
- 手机号不存在或已停用 → 统一响应「如该手机号已注册,验证码将在 1 分钟内发送」(防止枚举攻击)
- [ ] 同一手机号 1 小时内最多发送 **10 次**登录验证码(找回密码为独立计数,两者不共享限额);超限后提示「发送次数过多,请 1 小时后再试」
- [ ] 短信内容模板「【Fonrey 房睿】您的登录验证码为 {code}5 分钟内有效,请勿泄露。」
**步骤二:重置密码**
**登录校验逻辑**
- [ ] 点击「登录」,服务端校验 OTP
- 正确且未过期 → 登录成功,后续行为与 Story 2 密码登录完全一致(含 `is_initial_password` 判断)
- 错误 → 提示「验证码有误,请重新输入」;连续错误 ≥ 5 次 → 本次 OTP 作废,提示「验证码已失效,请重新获取」
- 已过期 → 提示「验证码已过期,请重新获取」
- [ ] **账号被锁定**(密码登录失败次数触发):验证码登录仍受账号锁定限制,锁定期间无法通过任何方式登录,提示「账号已被临时锁定,请 30 分钟后重试,或联系管理员解锁」
> **设计说明**:账号锁定是账号维度的安全策略,不区分登录方式;否则锁定形同虚设。
- [ ] **账号已停用**:提示「账号已停用,请联系您的管理员」
- [ ] 用户点击邮件中的链接,跳转至「重置密码」页面(链接含加密 Token服务端校验 Token 有效性)
- [ ] Token 无效或已过期 → 显示「链接已过期或已使用,请重新申请」,提供「重新申请」按钮
- [ ] 页面包含:新密码输入框、确认新密码输入框
- [ ] **密码复杂度规则**(符合安全基线):
- 长度 8 ~ 32 位
- 必须包含字母(区分大小写)和数字
- 建议包含特殊符号(非强制,但页面提示推荐)
- 不得与最近 3 次历史密码相同
- [ ] 两次密码输入不一致 → 提示「两次密码输入不一致」
- [ ] 不符合复杂度 → 实时提示具体不满足的规则(逐条校验,红色 × / 绿色 ✓ 视觉指引)
- [ ] 提交成功 → 显示「密码已重置,请使用新密码登录」,自动跳转至登录界面;原所有 Session 立即失效(强制重新登录)
---
### Story 5预留——手机验证码登录接口预留v2 实现)
**As** 绑定了手机号的经纪人,**I want** 通过手机号 + 短信验证码快速登录,**So that** 在忘记密码时仍能正常登录系统。
**当前状态**:本期 UI 入口以「即将开放」禁用态展示于登录界面,接口定义预留,不开放实际功能。
**预留接口设计**(供后端提前规划):
**接口规范**
```
POST /api/auth/login/phone/
Request: { phone: string, sms_code: string, tenant_id: string }
Response: { token: string, user: {...} } | { error: string }
Request: { phone: string, sms_code: string }
Response: { token: string, is_initial_password: bool, user: {...} } | { error_code: string, message: string }
```
**绑定条件**v2 实现时的前置要求):
- 手机号必须先在「个人设置」中与用户名账号完成绑定并通过验证
- 一个手机号只能绑定一个用户名账号(同一租户内)
- 绑定手机号后,可通过手机号 + 短信验证码联合登录
---
### Story 6预留——微信扫码登录接口预留v2 实现)
@@ -221,9 +253,9 @@ POST /api/auth/wechat/callback/ # 微信扫码确认后回调,换取系
```
客户端启动
├─ 本地有 Tenant ID 缓存?
├─ 本地有 Tenant Code 缓存?
│ │
│ YES ──→ 校验缓存 Tenant ID 是否仍有效(服务端 validate
│ YES ──→ 校验缓存 Tenant Code 是否仍有效(服务端 validate
│ │
│ 有效 ──→ 直接进入登录界面
│ │
@@ -231,33 +263,33 @@ POST /api/auth/wechat/callback/ # 微信扫码确认后回调,换取系
└─ NO ──→ 显示 Tenant 识别界面
用户输入 Tenant ID → 发起验证
用户输入 Tenant Code → 发起验证
验证成功 ──→ 缓存 Tenant ID → 进入登录界面
验证成功 ──→ 缓存 Tenant Code → 进入登录界面
验证失败 ──→ 显示错误信息,保持识别界面
```
#### 5.1.2 Tenant 识别界面规范
| 元素 | 规格 |
|------|------|
| 页面背景 | 品牌色渐变(与登录界面保持一致的视觉风格) |
| Logo | Fonrey 产品 Logo居中显示 |
| 标题 | 「欢迎使用 Fonrey 房睿」 |
| 副标题 | 「请输入您公司的专属识别码以继续」 |
| Tenant ID 输入框 | 单行数字输入,固定 12 位,支持粘贴;非数字字符自动过滤,超出 12 位截断 |
| 输入框 Label | 「公司识别码Tenant ID |
| 确认按钮 | 主色调按钮,文字「确认」 |
| 错误提示 | 输入框下方红色文字,固定区域占位(不影响布局抖动) |
| 帮助文案 | 「不知道识别码?请联系您公司的系统管理员」 |
| 元素 | 规格 |
| ------------- | --------------------------------------- |
| 页面背景 | 品牌色渐变(与登录界面保持一致的视觉风格) |
| Logo | Fonrey 产品 Logo居中显示 |
| 标题 | 「欢迎使用 Fonrey 房睿」 |
| 副标题 | 「请输入您公司的专属识别码以继续」 |
| Tenant Code 输入框 | 单行数字输入,固定 12 位,支持粘贴;非数字字符自动过滤,超出 12 位截断 |
| 输入框 Label | 「公司识别码Tenant Code |
| 确认按钮 | 主色调按钮,文字「确认」 |
| 错误提示 | 输入框下方红色文字,固定区域占位(不影响布局抖动) |
| 帮助文案 | 「不知道识别码?请联系您公司的Tenant Admin租户管理员 |
#### 5.1.3 Tenant ID 格式规范
#### 5.1.3 Tenant Code 格式规范
- **格式**:固定 **12 位纯数字**,如 `202500010001`
- **生成规则**(建议):由平台运营在系统管理后台开通租户时自动生成,不允许手动指定,确保全局唯一性;可采用时间戳前缀 + 随机后缀的方式生成(如 `YYYYMM` + 6 位随机数)
- **前端校验**:输入框仅接受数字字符(非数字自动过滤),输入满 12 位后自动触发格式完成状态;少于 12 位时点击「确认」弹出提示「识别码须为 12 位数字」
- **唯一性**:全局唯一(公共 Schema 层面),同一 Tenant ID 不可分配给多个租户
- **唯一性**:全局唯一(公共 Schema 层面),同一 Tenant Code 不可分配给多个租户
- **客户端存储**Electron `app.getPath('userData')` 目录下的配置文件(加密存储,防止明文读取)
#### 5.1.4 服务端 Tenant 验证接口规范
@@ -267,7 +299,7 @@ POST /api/auth/tenant/verify/
Request Body:
{
"tenant_id": "202500010001"
"tenant_code": "202500010001"
}
Response 200 (成功):
@@ -294,31 +326,48 @@ Response 200 (失败):
#### 5.2.1 界面布局
登录界面顶部以 **Tab 切换**区分两种登录方式「密码登录」默认选中Tab 下方的表单区随当前选中 Tab 动态切换,微信扫码作为独立的「其他登录」保持禁用。
```
┌─────────────────────────────────────────┐
│ [租户 Logo] [租户公司名称] │ ← 顶部品牌区Tenant 识别后回填)
│ │
│ ┌──────────────┬──────────────────┐ │
│ │ 密码登录 ✓ │ 验证码登录 │ │ ← 登录方式 Tab默认选中「密码登录」
│ └──────────────┴──────────────────┘ │
│ │
│ ── 密码登录 Tab默认展示─────────── │
│ ┌───────────────────────┐ │
│ │ 用户名 │ │
│ │ 手机号 │ │
│ └───────────────────────┘ │
│ ┌───────────────────────┐ │
│ │ 密码 👁 │ │
│ └───────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ [背景图 + 拼图缺口] 🔄 │ │ ← 右上角刷新图标
│ │ │ │
│ │ [拼图碎片] │ │
│ │ ├────────────────────────────── │ │
│ │ ◀ 拖动滑块完成拼图 ▶ │ │
│ │ [拼图碎片] ◀ 拖动滑块完成拼图 ▶│ │
│ └─────────────────────────────────┘ │
│ ┌───────────────────────┐ │
│ │ 登 录 │ │ ← 主 CTA橙色
│ └───────────────────────┘ │
│ 忘记密码 │ ← 文字链接(忘记用户名入口已废弃)
│ │
忘记用户名 忘记密码 │ ← 次级入口,文字链接
── 验证码登录 Tab切换后展示──────── │
│ ┌───────────────────────┐ │
│ │ 手机号 │ │
│ └───────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ [拼图验证] ← 先完成验证,再获取 │ │ ← 验证码登录下滑块前置
│ └─────────────────────────────────┘ │
│ ┌──────────────────┐ ┌─────────────┐ │
│ │ 验证码6 位) │ │ 获取验证码 │ │ ← 通过滑块后按钮可点击60s 冷却
│ └──────────────────┘ └─────────────┘ │
│ ┌───────────────────────┐ │
│ │ 登 录 │ │
│ └───────────────────────┘ │
│ 忘记密码 │
│ │
│ ─────────────── 其他登录 ──────────────│
│ [手机验证码登录 - 即将开放] │ ← 禁用态,灰色
│ [微信扫码登录 - 即将开放] │ ← 禁用态,灰色
│ │
│ 切换公司 │ ← 底部,小字链接
@@ -335,7 +384,7 @@ Response 200 (失败):
| 验证码有效期 | 单次验证会话有效,提交登录后服务端 Token 立即失效;超过 3 分钟未操作需重新加载 |
| 验证失败处理 | 拼图区域抖动动画提示,自动刷新新背景图;**不计入账号密码错误次数**(行为验证失败属独立事件) |
| 密码错误锁定 | 同一账号连续密码错误 ≥ 5 次,锁定 30 分钟;解锁方式:等待超时自动解锁 或 管理员手动解锁 |
| 密码错误计数 | 计数存于 RedisKey 格式:`login_fail:tenant_id:username`TTL 30 分钟 |
| 密码错误计数 | 计数存于 RedisKey 格式:`login_fail:tenant_id:phone`phone 即用户名/手机号)TTL 30 分钟 |
| 验证码刷新 | 登录失败(用户名/密码错误)后自动刷新拼图;用户亦可主动点击「刷新」图标重新加载背景图 |
| HTTPS | 所有登录相关请求强制 HTTPS不允许 HTTP 降级 |
| 密码传输 | 前端不做密码加密HTTPS 层保证传输安全;后端存储使用 `django.contrib.auth` 默认的 `PBKDF2+SHA256` 哈希 |
@@ -355,18 +404,19 @@ Response 200 (失败):
系统内共有两类账号创建场景,权限和规则各不相同:
** Tenant Admin 账号(每个租户唯一的超级管理账号)**
** Tenant Admin 账号(每个租户的超级管理账号)**
| 项目 | 规格 |
|------|------|
| 创建时机 | 平台运营在系统管理后台开通租户时,同步创建第一个 Tenant Admin 账号 |
| 用户名 | **由平台运营自定义设置**,格式:英文字母开头,仅含字母/数字/下划线6~30 字符,同租户内唯一 |
| 初始密码 | **由平台运营自定义设置**须符合密码复杂度规则8~32 位,含字母+数字) |
| 首次登录 | 强制修改初始密码,不可跳过 |
| 权限范围 | 拥有该租户内最高权限,可管理员工账号、角色、系统设置等 |
| 数量限制 | 每个租户仅限 1 个 Tenant Admin 账号后续可扩展为多管理员v2 规划) |
| 项目 | 规格 |
| ---- | ------------------------------------------------------------ |
| 创建时机 | 平台运营在系统管理后台开通租户时,系统**自动**以该租户联系人手机号创建 Tenant Admin 账号,无需手动设置 |
| 用户名 | **固定为该租户联系人的手机号**11 位数字),全局唯一,创建后不可更改 |
| 初始密码 | **系统统一固定初始密码**(与普通员工相同,由平台在部署配置中设定,如 `Fonrey@2025` |
| 首次登录 | 强制修改初始密码,不可跳过 |
| 权限范围 | 拥有该租户内最高权限,可管理员工账号、角色、系统设置等 |
| 数量限制 | 每个租户仅限 1 个 Tenant Admin 账号后续可扩展为多管理员v2 规划) |
| 数据来源 | 联系人手机号来自 `public.tenants.contact_phone` 字段,开通租户时由平台运营录入,必填 |
** 普通员工账号(经纪人、店长、行政等)**
** 普通员工账号(经纪人、店长、行政等)**
| 项目 | 规格 |
|------|------|
@@ -381,10 +431,10 @@ Response 200 (失败):
| 字段 | 类型 | Tenant Admin | 普通员工账号 | 说明 |
|------|------|-------------|-------------|------|
| 用户名username | CharField(30) | 平台运营自定义,字母开头,含字母/数字/下划线6~30 字符 | **固定为员工手机号**11 位数字) | 登录 ID创建后不可更改 |
| 密码password | CharField | 平台运营自定义初始密码 | **系统统一固定初始密码** | PBKDF2+SHA256 哈希存储 |
| 手机号phone | CharField(11) | 选填,加密存储 | **必填,同时作为用户名**,加密存储,同租户内唯一 | 当前阶段为登录 IDv2 启用手机验证码登录后复用此字段 |
| 邮箱email | EmailField | 选填,同租户唯一 | 选填,同租户唯一 | 用于找回密码;若为空则无法自助找回 |
| 用户名username | CharField(30) | **固定为联系人手机号**11 位数字) | **固定为员工手机号**11 位数字) | 登录 ID创建后不可更改;两类账号规则统一 |
| 密码password | CharField | **系统统一固定初始密码** | **系统统一固定初始密码** | PBKDF2+SHA256 哈希存储;首次登录强制修改 |
| 手机号phone | CharField(11) | **必填,同时作为用户名**,来源于 `public.tenants.contact_phone` | **必填,同时作为用户名**,加密存储,同租户内唯一 | 两类账号均用手机号登录,v2 启用手机验证码后复用此字段 |
| 邮箱email | EmailField | 选填,同租户唯一 | 选填,同租户唯一 | 在本系统无必须业务用途,完全可选;普通员工忘记密码通过手机短信验证码自助找回,**与邮箱无关** |
| 员工档案关联staff_id | OneToOneField → `org.Staff` | 可选关联(平台运营账号) | 必须关联 | 实名绑定 |
| 账号状态status | CharField | `active` / `disabled` / `locked` | `active` / `disabled` / `locked` | locked 为密码错误锁定30 分钟自动恢复 |
| 初始密码标记is_initial_password | BooleanField | True首次登录前 | True首次登录前 | True 时登录成功后强制跳转修改密码页 |
@@ -411,109 +461,84 @@ Response 200 (失败):
---
### 5.4 找回流程详细说明
### 5.4 找回密码详细说明
#### 5.4.1 找回用户名流程
> **说明**Story 4「找回用户名」已废弃。普通员工用户名固定为手机号无需找回Tenant Admin 如忘记用户名请联系平台运营线下处理。
> **说明**:由于普通员工的用户名即为其**手机号**,通常无需「找回用户名」功能。登录界面的「忘记用户名」入口保留,但仅对 Tenant Admin 账号有意义(其用户名为自定义字符串)。
```
用户点击「忘记用户名」
├─ 普通员工:提示「您的登录账号为您的手机号,请直接使用手机号登录」
│ 提供「返回登录」按钮
└─ Tenant Admin用户名非手机号格式
├─ 输入绑定邮箱
│ │
│ 服务端查询(不向前端返回查询结果,防止枚举)
│ │
│ 统一响应「如该邮箱已绑定账号,您将收到邮件」
│ │
│ 后台:邮箱存在 → 发送邮件(包含用户名)
│ 邮箱不存在 → 静默处理
└─ 用户查收邮件,获取用户名 → 返回登录
```
![[找回用户名流程.png]]
> **前端识别逻辑**:用户在「忘记用户名」页面输入邮箱提交后,服务端根据是否匹配到 Tenant Admin 账号决定处理路径,前端无需区分,统一展示「如该邮箱已绑定账号,您将收到邮件」。
**邮件模板(找回用户名)**
```
主题:您的 Fonrey 房睿用户名
您好,
您请求找回在 [公司名称] 的 Fonrey 账号用户名。
您的用户名为:{username}
如果这不是您的操作,请忽略此邮件。如有疑问,请联系您的系统管理员。
此邮件由系统自动发送,请勿回复。
发送时间:{datetime}
```
#### 5.4.2 找回密码流程
#### 5.4.1 找回密码流程
```
用户点击「忘记密码」
步骤1身份验证
步骤1输入手机号
├─ 输入手机号(即用户名)+ 绑定邮箱
Tenant Admin 则输入自定义用户名 + 绑定邮箱)
├─ 输入 11 位手机号,点击「获取验证码」
│ │
│ 服务端校验用户名与邮箱是否匹配
│ 服务端校验手机号是否存在且状态为 active
│ │
│ 统一响应「如信息匹配,重置链接将发送至您的邮箱」(防止枚举)
│ 统一响应「如该手机号已注册,验证码将在 1 分钟内发送」(防止枚举)
│ │
│ 后台:匹配成功 → 生成加密 Token有效期 30min→ 异步发送邮件
│ 不匹配 → 静默处理
│ 后台:存在且 active → 生成 6 位 OTP有效期 10 分钟,存入 sms_otp_records → 发送短信
│ 不存在或已停用 → 静默处理
步骤2用户点击邮件中的重置链接
步骤2输入短信验证码
├─ 服务端校验 Token 有效性
├─ 输入 6 位验证码,点击「下一步」
│ │
有效 → 展示「重置密码」表单
服务端校验 OTP
│ │
无效/过期 → 提示「链接已过期,请重新申请」,提供「重新申请」按钮
正确且未过期 → 颁发一次性 sms_reset_token有效期 15 分钟)→ 进入步骤3
│ │
│ 错误(累计 < 5 次)→ 提示「验证码有误,请重新输入」
│ 错误(累计 ≥ 5 次)→ 提示「验证已失败,请重新获取验证码」,本次 OTP 作废
│ 已过期 → 提示「验证码已过期,请重新获取」
步骤3用户输入并提交新密码
步骤3重置密码
├─ 密码复杂度校验(≥ 8 位,含字母+数字)
├─ 与历史密码对比校验(最近 3 次,含固定初始密码)
├─ 页面携带 sms_reset_token服务端校验有效性
│ │
│ 无效/过期 → 提示「操作已超时请重新发起找回密码」跳回步骤1
│ │
├─ 用户输入新密码 + 确认新密码,实时逐条校验复杂度规则(✓/✗)
└─ 校验通过 → 更新密码is_initial_password = False
→ 清除该账号所有有效 Session强制重新登录
→ 跳转登录界面,提示「密码已重置,请重新登录
```
![[找回密码流程.png]]
> **注意**:找回密码流程依赖员工账号绑定了邮箱。若员工未绑定邮箱,无法自助找回,需联系 Tenant Admin 在管理界面执行「重置密码」操作,将密码恢复为固定初始密码。
**邮件模板(重置密码)**
```
主题:重置您的 Fonrey 房睿密码
您好,
我们收到了重置您在 [公司名称] 的 Fonrey 账号密码的请求。
请点击以下链接重置密码(链接 30 分钟内有效):
{reset_link}
如果您未发起此请求,请忽略此邮件,您的密码不会被更改。
此邮件由系统自动发送,请勿回复。
发送时间:{datetime}
└─ 提交成功
→ 更新密码is_initial_password = False
→ 清除该账号所有有效 Session强制重新登录
→ 跳转登录界面,提示「密码已重置,请使用新密码登录」
```
---
### 5.5 后端数据模型设计
### 5.5 手机验证码登录详细说明
> 本节为 Story 5 的实现规范补充。短信基础设施(`sms_otp_records` 表、OTP 发送/校验逻辑)在 Story 3 找回密码中已建设完成,本节描述在**登录场景**下复用该基础设施时的关键差异点。
**与找回密码短信逻辑的差异对比**
| 维度 | 找回密码Story 3 | 验证码登录Story 5 |
|------|-------------------|---------------------|
| `scene` 字段 | `password_reset` | `login` |
| OTP 有效期 | 10 分钟 | 5 分钟 |
| 每小时发送上限 | 5 次 | 10 次 |
| 验证成功后动作 | 颁发 `sms_reset_token` → 步骤三重置密码 | 直接颁发 Session Token登录成功 |
| 短信文案 | 「密码重置验证码」 | 「登录验证码」 |
| 账号锁定影响 | 不受密码错误锁定限制(非密码登录路径) | **受账号锁定限制**(账号维度安全策略,不区分方式)|
**滑块验证前置规则**(验证码登录特有):
- 用户须先完成滑块拼图验证,「获取验证码」按钮方可点击
- 滑块验证通过后,拼图区域保持「验证通过」状态,不需要在点击「登录」前再次验证
- 切换 Tab 时,滑块验证状态重置(须重新完成验证后方可获取验证码)
**`sms_otp_records` 表复用说明**
- 不新建表,复用 `DATA_MODEL_LOGIN.md` 中定义的 `sms_otp_records`
- `scene` 字段区分场景:`login` / `password_reset`,各自独立限流计数
- 同一手机号同一 scene 同一时间只有一条有效 OTP新发送时将旧记录标记为 `used`
---
### 5.6 后端数据模型设计
> **数据模型已迁移至独立文档**,请参阅:
> **`Project/fonrey/DATA_MODEL/DATA_MODEL_LOGIN.md`**
@@ -521,7 +546,7 @@ Response 200 (失败):
该文档包含:
- `user_accounts` 账号主表完整字段定义、约束、索引、Django Model 代码)
- `login_attempts` 登录审计表
- `password_reset_tokens` 密码重置令牌表
- `sms_otp_records` 短信验证码记录表(找回密码 + 验证码登录共用)
- `password_histories` 历史密码记录表
- Redis 缓存结构说明
- 账号状态机与创建流程
@@ -531,13 +556,13 @@ Response 200 (失败):
---
### 5.6 Electron 客户端登录相关约定
### 5.7 Electron 客户端登录相关约定
| 约定项 | 规格 |
|--------|------|
| Tenant ID 存储 | `electron-store``app.getPath('userData')` + AES 加密,不存储明文 |
| Tenant Code 存储 | `electron-store``app.getPath('userData')` + AES 加密,不存储明文 |
| Session Token 存储 | 内存(`global` 变量)+ `session` CookieChromium 管理),不写入磁盘明文文件 |
| 登录页加载 | 客户端主进程根据 Tenant ID 构建目标 URL`https://{tenant_slug}.fonrey.com/auth/login/`),通过 `BrowserWindow.loadURL()` 加载 |
| 登录页加载 | 客户端主进程根据 Tenant Code 构建目标 URL`https://{tenant_slug}.fonrey.com/auth/login/`),通过 `BrowserWindow.loadURL()` 加载 |
| 多标签页处理 | 同一 `BrowserWindow` 内,所有页面共享同一 Session Cookie |
| 客户端登出 | 调用服务端 `POST /api/auth/logout/` 使服务端 Session 失效 + 清除 Chromium Session Cookie |
| 窗口关闭时 | Session 保留(不自动登出),下次打开客户端时若 Session 未过期,直接进入系统 |
@@ -553,8 +578,9 @@ Response 200 (失败):
|--------|------|------|
| `django.contrib.auth` | 用户认证基础框架 | 扩展 `AbstractBaseUser` 而非直接使用 `User` 模型,以支持 `username` 唯一性约束在租户维度而非全局 |
| `django-tenants` | 多租户隔离 | `UserAccount` 属于租户级 SchemaTenant 验证接口属于 `shared_apps` |
| `Redis` | 滑块验证 Token 存储、登录失败计数、密码重置 Token 缓存 | 验证 Key`captcha_token:{uuid}`TTL 3min登录失败 Key`login_fail:{tenant_id}:{username}` |
| `Celery` | 发送找回邮件 | 邮件发送异步处理,防止接口响应超时 |
| `Redis` | 滑块验证 Token 存储、登录失败计数、短信 OTP 限流计数 | 验证 Key`captcha_token:{uuid}`TTL 3min登录失败 Key`login_fail:{tenant_id}:{username}`OTP 限流 Key`sms_limit:{scene}:{phone}`TTL 1h|
| 短信服务(待选型) | 发送登录验证码 / 找回密码验证码 | 国内需选用具备短信资质的服务商(如阿里云短信、腾讯云短信);需申请短信签名和模板审核 |
| `Celery` | 异步任务处理 | 短信发送异步处理,防止接口响应超时;原邮件发送需求已废弃,短信为主要通知方式 |
| `django-ratelimit` 或自定义中间件 | 接口限流 | Tenant 验证接口、登录接口、找回密码接口均需限流 |
| `Pillow` | 滑块拼图图片处理 | 生成拼图背景图(抠出缺口区域)及对应的拼图碎片图片,输出为 Base64分别通过两个字段返回给前端 |
@@ -569,17 +595,17 @@ Response 200 (失败):
| 风险 | 可能性 | 影响 | 缓解措施 |
|------|--------|------|---------|
| 滑块验证被机器模拟轨迹绕过 | 低 | 高 | 服务端同时校验位置偏差 + 轨迹曲线特征(非线性运动特征),拒绝匀速/程序化轨迹;后续可引入设备指纹加固 |
| Tenant ID 枚举攻击(暴力试探) | 低 | 中 | Tenant 验证接口限流每IP每分钟≤10次返回结果不区分「未找到」与「已禁用」|
| 密码重置 Token 泄露 | 低 | 高 | Token 单次有效、30分钟过期、HTTPS 传输 |
| 邮件发送失败导致用户无法找回密码 | 中 | | 邮件发送失败写入告警日志,管理员可通过后台查看 Token 手动告知用户 |
| Tenant Code 枚举攻击(暴力试探) | 低 | 中 | Tenant 验证接口限流每IP每分钟≤10次返回结果不区分「未找到」与「已禁用」|
| 密码重置 Token 泄露 | 低 | 高 | `sms_reset_token` 单次有效、15 分钟过期、HTTPS 传输 |
| 短信服务故障导致用户无法找回密码或验证码登录 | 中 | | 短信发送失败写入告警日志;密码登录作为保底方式(非单一入口);建议配置备用短信服务商通道 |
| 多端同时登录同一账号 | 高(日常场景) | 低 | 本期允许,后续如需踢出,可在 Token 机制中引入版本号 |
### 6.4 开放问题(开发前需确认)
- [ ] **邮件服务商选型**:使用 SendGrid / 阿里云邮件推送 / SMTP 自建?需运维确认 — 负责人:后端负责人 — 截止:开发启动前
- [ ] **短信服务商选型**:使用阿里云短信 / 腾讯云短信 / 其他服务商?需运维确认并提前申请短信签名和模板审核(国内审核周期 13 个工作日)— 负责人:后端负责人 — 截止:开发启动前
- [ ] **Session 有效期默认值**8 小时是否满足各租户需求?是否允许租户管理员自行配置?— 负责人:产品经理 — 截止:开发启动前
- [ ] **滑块拼图实现方案**自研Pillow 生成图片 + 前端拖拽组件)还是集成第三方行为验证服务(如极验 GeeTest / 网易易盾)?自研可控但需维护图库;第三方开箱即用但引入外部依赖,需评估数据合规要求 — 负责人:后端负责人 + 安全 — 截止:开发启动前
- [ ] **账号锁定通知**:账号被锁定后,是否自动发邮件通知用户和/或管理员?— 负责人:产品经理 — 截止:开发启动前
- [ ] **账号锁定通知**:账号被锁定后,是否自动发短信通知用户和/或通知管理员(站内消息)?— 负责人:产品经理 — 截止:开发启动前
- [ ] **历史密码校验范围**:最近 3 次是否足够?是否需要额外规则(如不能与用户名相同)?— 负责人:产品经理 — 截止:开发启动前
---
@@ -602,7 +628,7 @@ Response 200 (失败):
```
[未识别 Tenant]
│ 输入有效 Tenant ID
│ 输入有效 Tenant Code
[未登录]
│ 账密登录成功
@@ -625,24 +651,20 @@ Response 200 (失败):
| 接口 | 方法 | Schema 位置 | 是否需要鉴权 | 说明 |
|------|------|------------|------------|------|
| `/api/auth/tenant/verify/` | POST | Publicshared | 否 | Tenant ID 验证 |
| `/api/auth/tenant/verify/` | POST | Publicshared | 否 | Tenant Code 验证 |
| `/api/auth/captcha/` | GET | Tenant | 否 | 获取滑块拼图验证码(返回背景图 Base64 + 碎片图 Base64 + 验证 Token |
| `/api/auth/captcha/verify/` | POST | Tenant | 否 | 提交滑动轨迹 + 位置,服务端校验并返回一次性通过凭证(供登录接口使用) |
| `/api/auth/login/` | POST | Tenant | 否 | 账号密码登录 |
| `/api/auth/login/` | POST | Tenant | 否 | 手机号 + 密码登录 |
| `/api/auth/login/phone/` | POST | Tenant | 否 | 手机号 + 短信验证码登录MVP 正式功能) |
| `/api/auth/logout/` | POST | Tenant | 是 | 登出,使 Session 失效 |
| `/api/auth/recover/username/` | POST | Tenant | 否 | 发起找回用户名 |
| `/api/auth/recover/password/request/` | POST | Tenant | 否 | 发起找回密码(发送邮件) |
| `/api/auth/recover/password/request/` | POST | Tenant | 否 | 发起找回密码(发送短信验证码) |
| `/api/auth/recover/password/verify/` | POST | Tenant | 否 | 校验短信验证码,颁发一次性 `sms_reset_token` |
| `/api/auth/recover/password/reset/` | POST | Tenant | 否Token 鉴权) | 提交新密码 |
| `/api/auth/login/phone/` | POST | Tenant | 否 | **预留**手机验证码登录 |
| `/api/auth/wechat/qrcode/` | GET | Tenant | 否 | **预留**获取微信二维码 |
| `/api/auth/wechat/callback/` | POST | Tenant | 否 | **预留**,微信扫码回调 |
| `/api/auth/wechat/qrcode/` | GET | Tenant | 否 | **预留 v2**获取微信二维码 |
| `/api/auth/wechat/callback/` | POST | Tenant | 否 | **预留 v2**微信扫码回调 |
### 8.3 相关文档参考
- 客户端发布管理模块 PRD`Project/fonrey/PRD/发布管理/客户端发布管理模块PRD.md`
- 组织人事管理模块 PRD`Project/fonrey/PRD/组织人事管理/组织人事管理模块PRD.md`
- 权限管理模块 PRD`Project/fonrey/PRD/权限管理/权限管理模块PRD.md`
- 系统管理模块 PRD`Project/fonrey/PRD/系统管理/系统管理模块PRD.md`
- 技术栈文档:`Project/fonrey/TECH_STACK/TECH_STACK.md`
- **登录管理数据模型**`Project/fonrey/DATA_MODEL/DATA_MODEL_LOGIN.md`
- **登录管理技术方案**`Project/fonrey/TECH_STACK/登录管理技术方案.md`