> **For AI assistants**: Read this entire file before writing any code. All decisions here are final. Do not suggest alternatives unless asked. # Fonrey 登录管理技术方案 **版本**: 3.1 **项目**: Fonrey 房产经纪管理系统 **技术栈**: Django 4.x + HTMX + Alpine.js + PostgreSQL 16 + Redis + Celery **关联 PRD**: `PRD/登录管理/用户登录管理模块PRD.md` **关联数据模型**: `DATA_MODEL/DATA_MODEL_LOGIN.md`(本方案不重复 DDL) **关联契约规范**: `TECH_STACK/API_CONTRACT.md`(全局 API 契约权威) **最后更新**: 2026-04-27 --- ## 一、文档定位与边界 本文件仅定义登录模块的: 1. 模块边界与服务职责 2. API 端点设计(页面 / HTMX / JSON) 3. 登录安全策略(验证码、锁定、会话、找回) 4. 缓存与异步任务策略 5. 错误码与测试映射 > 不在本文件展开表字段、索引、DDL。数据结构以 `DATA_MODEL_LOGIN.md` 为唯一权威。 --- ## 二、范围定义(以 P0 为准) ### 2.1 P0 必须覆盖 - Tenant ID 校验(Public Schema) - 用户名 + 密码登录(Tenant Schema) - 验证码挑战与一次性 pass token - 连续失败锁定与解锁机制 - 找回用户名 / 重置密码 - 首次登录强制改密 - 安全登出与会话销毁 ### 2.2 预留(非本期强制) - MFA(OTP / 短信) - 企业 SSO(OAuth2 / SAML) - 设备指纹与风险评分 --- ## 三、模块架构边界 ## 3.1 模块职责(`apps/account`) - 登录认证入口与 Session 建立 - 密码策略、锁定策略、登录防刷策略执行 - 找回流程与一次性重置令牌管理 - 登录/登出/失败审计事件写入 ## 3.2 多租户分层职责 | 层级 | Schema | 职责 | |---|---|---| | Tenant ID 校验 | Public | 校验租户标识、域名映射、租户状态 | | 登录认证 | Tenant | 账号鉴权、失败计数、会话建立 | | 密码找回 | Tenant | 身份确认、令牌签发与核销 | ## 3.3 外部依赖 | 依赖模块 | 用途 | |---|---| | `apps/org` | 员工状态联动(离职/冻结禁止登录) | | `core/encryption.py` | 手机号等敏感信息加密与脱敏 | | `core/cache.py` | 验证码票据、失败计数、频控缓存 | | `Celery` | 找回消息异步发送、安全日报任务 | --- ## 四、API 设计原则 1. 登录链路固定三段:Tenant 校验 → 验证码 → 账号认证。 2. 最小暴露原则:账号不存在与密码错误统一返回。 3. 认证状态以数据库为准,Redis 仅做加速与频控。 4. 安全相关写操作后立即失效缓存,不依赖 TTL。 5. HTMX 返回片段,JSON 返回结构化错误体。 --- ## 五、端点清单(核心) ## 5.1 页面路由(SSR) | 路径 | 方法 | 鉴权 | 说明 | |---|---|---|---| | `/account/tenant/verify/` | GET | 否 | 首次租户识别页 | | `/account/login/` | GET | 否 | 登录页 | | `/account/change-password/` | GET | 是 | 首登强制改密页 | | `/account/recover-username/` | GET | 否 | 找回用户名页 | | `/account/recover-password/` | GET | 否 | 找回密码页 | ## 5.2 HTMX 片段端点 | 路径 | 方法 | 用途 | 返回 | |---|---|---|---| | `/account/fragments/login-form/` | GET | 登录表单局刷 | HTML 片段 | | `/account/fragments/captcha/` | GET | 验证码区块刷新 | HTML 片段 | | `/account/fragments/recover-step/` | GET | 找回步骤局刷 | HTML 片段 | ## 5.3 JSON API(P0) | 端点 | 方法 | 说明 | |---|---|---| | `/api/account/tenant/verify/` | POST | 校验 tenant_id 与可用性 | | `/api/account/captcha/generate/` | POST | 生成验证码 challenge | | `/api/account/captcha/verify/` | POST | 校验 challenge 并签发 pass token | | `/api/account/login/` | POST | 登录并建立 session | | `/api/account/logout/` | POST | 登出并销毁 session | | `/api/account/password/change/` | POST | 登录态改密 | | `/api/account/username/recover/` | POST | 找回用户名 | | `/api/account/password/recover/request/` | POST | 申请重置密码令牌 | | `/api/account/password/recover/reset/` | POST | 使用令牌重置密码 | --- ## 六、关键 API 规范(请求/响应) ## 6.1 登录 `POST /api/account/login/` ```json { "tenant_id": "fonrey-sh", "username": "agent_001", "password": "******", "captcha_pass_token": "token" } ``` 成功 `200`: ```json { "message": "登录成功", "redirect_url": "/home/" } ``` 失败码:`ACCOUNT_LOGIN_INVALID_CREDENTIAL` / `ACCOUNT_LOCKED` / `ACCOUNT_CAPTCHA_INVALID` ## 6.2 申请重置密码 `POST /api/account/password/recover/request/` ```json { "tenant_id": "fonrey-sh", "username": "agent_001", "contact": "138****0000" } ``` 规则: - 频率限制(建议 5 次/小时) - 统一返回文案,避免枚举账号存在性 --- ## 七、HTMX 交互约定 ## 7.1 Header 约定 - 请求头:`HX-Request: true` - 成功触发:`HX-Trigger: {"toast":{"level":"success","message":"操作成功"}}` - 失败触发:`HX-Trigger: {"toast":{"level":"error","message":"操作失败"}}` - 登录成功:`HX-Redirect: /home/` ## 7.2 模板分片命名 - `templates/account/fragments/login_form.html` - `templates/account/fragments/captcha_panel.html` - `templates/account/fragments/recover_step.html` --- ## 八、权限与数据范围 ## 8.1 访问控制 - 匿名可访问:租户校验、登录、找回相关端点 - 登录后访问:改密、登出 - 首登未改密用户仅允许访问改密页面 ## 8.2 审计字段要求 登录与找回相关操作至少记录: - tenant_schema - username(或脱敏标识) - ip / user_agent - result_code - created_at --- ## 九、异步任务与缓存策略 ## 9.1 Celery 任务 | 任务 | 触发时机 | 说明 | |---|---|---| | `account_send_recover_message_task` | 找回请求成功后 | 异步发送邮件/短信 | | `account_security_digest_task` | 定时任务 | 汇总锁定、失败、异常登录统计 | ## 9.2 Redis Key 规范 | Key | TTL | 说明 | |---|---|---| | `{schema}:account:captcha:{challenge_id}` | 180s | 验证码挑战态 | | `{schema}:account:captcha:pass:{token}` | 180s | 验证通过一次性票据 | | `{schema}:account:login_fail:{username}` | 1800s | 登录失败计数 | | `{schema}:account:recover:rate:{username}` | 3600s | 找回频控 | --- ## 十、性能与可靠性约束 - 登录链路接口目标:`p95 < 300ms`(不含外部消息发送) - 找回请求接口目标:`p95 < 400ms`(消息发送异步化) - 缓存故障时系统应降级到 DB 校验,但保留频控硬兜底 - 验证码组件不可用时,应返回明确错误并禁止跳过登录防护 --- ## 十一、安全与合规 1. 密码仅允许 Django 安全哈希(PBKDF2 / Argon2)。 2. 连续失败 N 次锁定(建议 5 次,30 分钟)。 3. 重置令牌一次性使用,过期失效,不可复用。 4. 登出必须销毁会话,旧页面刷新需重定向登录。 5. 敏感字段仅脱敏返回,禁止明文日志输出。 --- ## 十二、错误码建议 | code | HTTP | 场景 | |---|---|---| | `ACCOUNT_TENANT_INVALID` | 400 | tenant 无效或停用 | | `ACCOUNT_CAPTCHA_INVALID` | 400 | 验证码失败/过期 | | `ACCOUNT_LOGIN_INVALID_CREDENTIAL` | 401 | 账号或密码错误 | | `ACCOUNT_LOCKED` | 423 | 账号锁定中 | | `ACCOUNT_PASSWORD_WEAK` | 400 | 新密码不满足复杂度 | | `ACCOUNT_RESET_TOKEN_INVALID` | 400 | 重置令牌无效/已使用 | --- ## 十三、测试映射(P0) | 场景 | 最低覆盖 | |---|---| | Tenant 校验 | 有效/无效/停用租户 | | 登录链路 | 验证码通过、失败锁定、解锁后登录 | | 找回流程 | 频控、令牌一次性、过期处理 | | 首登改密 | 未改密不可进入业务页 | | 登出 | 会话销毁、回退重定向 | 测试文件:`tests/integration/account/test_us_account.py` --- ## 十四、落地顺序建议 1. 先实现 tenant 校验 + 登录主链路 2. 再接验证码 + 失败锁定 3. 再实现找回用户名/密码 4. 最后补安全摘要任务与审计报表 --- ## 十五、文档同步规则 - 登录数据结构调整:同步 `DATA_MODEL_LOGIN.md` - 安全策略或门禁调整:同步 `权限管理系统技术方案.md` - API 变更:同步本文件与登录 PRD 验收条目