让用户顺利进来
注册流程的唯一 KPI 是注册转化率:每一步校验都必须在"拦住无效提交"和"别让用户烦"之间取舍。
这一页是给产品经理新人看的「账号体系说明书」。它不讲代码,只讲三件事:
用户会经历哪些流程、每种异常该怎么拦、
拦下来之后该怎么告诉用户。
页面里所有数值(60 秒、5 次、15 分钟、30 分钟…)都直接读取自
assets/js/config.js,所以文档和实现永远一致。
注册流程的唯一 KPI 是注册转化率:每一步校验都必须在"拦住无效提交"和"别让用户烦"之间取舍。
登录流程的核心是成功率与安全性的平衡:既要让正常用户一次登进来,也要挡住撞库与暴力破解。
找回密码是留存兜底。它做不好,用户不是"改天再来",而是直接换产品。
| 角色 | 核心诉求 | 典型场景 | 对产品的启发 |
|---|---|---|---|
| 新用户 | 尽快用上产品,不愿意填太多信息 | 用手机号注册,只想收一个验证码 | 字段越少越好;注册成功直接登录,别让用户再输一次 |
| 老用户 | 快速登录,长期免登录 | 回访时勾选「记住我」 | 提供 7 天免登录;登录态到期要有明确提示而不是静默失效 |
| 忘记密码的用户 | 找回账号,且过程必须可信 | 换手机后想不起来旧密码 | 验证身份 → 设置新密码分成两步,中途失败可重来,每一步都告知当前进度 |
| 恶意用户 / 脚本 | 批量注册、撞库、刷短信 | 用脚本高频请求验证码 | 必须有发送频率限制、每日上限、失败锁定、验证码等机制 |
| 客服 / 运营 | 能解释用户遇到的情况 | 用户反馈"我登不上,一直提示锁定" | 错误码体系要规范,客服能凭错误码快速定位原因 |
下面三张图用纯 HTML + CSS 绘制(没有引入任何图表库)。 深蓝圆角 = 开始/结束,浅蓝方块 = 处理步骤,黄色菱形 = 判断条件, 绿色 = 通过分支,红色 = 拦截分支。
画状态机的好处是:你能一眼看出"还有哪些状态没考虑提示"。 每多一个状态,就多一组"进入条件 + 退出条件 + 界面表现 + 提示文案"要定义。
正在读取本机数据…
这块面板直接读取你浏览器里的 localStorage(键名前缀
),每秒刷新一次。
试着在另一个标签页注册、登录或故意输错密码,回到这里就能看到状态跟着变。
| 起始状态 | 触发事件 | 目标状态 | 界面表现与提示 |
|---|---|---|---|
| 未注册 | 完成注册(验证码 + 密码 + 协议全部通过) | 已登录 | 提示「注册成功,已为你自动登录」并跳转个人中心 |
| 已注册 | 密码正确 + 未处于锁定 | 已登录 | 提示「登录成功」,写入登录态,跳转个人中心或 redirect 页面 |
| 已注册 | 密码错误,失败次数 < 5 | 已注册 | inline 标红密码框,提示「密码错误,还可以尝试 N 次」 |
| 已注册 | 密码错误,失败次数 = 5 | 锁定 | 顶部红色横幅 + 15 分钟倒计时,提交按钮禁用 |
| 锁定 | 锁定时间到期 | 已注册 | 横幅转为「锁定已解除,现在可以重新登录了」,按钮恢复可用 |
| 已登录 | 点击退出登录(二次确认) | 已注册 | 提示「已安全退出登录」并回到首页;其他标签页下次检查时同步退出 |
| 已登录 | 登录态到期 / 本地数据被清除 | 已注册 | 提示「登录状态已过期」并跳转登录页,携带 redirect 便于登录后返回 |
| 已注册 | 找回密码验证通过 | 重置中 | 生成 30 分钟凭证,跳转设置新密码页并显示剩余有效时间 |
| 重置中 | 凭证过期 / 链接无效 / 直接访问无 token | 已注册 | 整页错误态说明原因,3 秒后自动跳回找回密码页,避免死页面 |
| 重置中 | 提交合规的新密码(与旧密码不同) | 已重置 | 提示「密码已重置,请使用新密码登录」,作废凭证并清除登录态 |
| 已重置 | 使用新密码登录成功 | 已登录 | 正常进入个人中心,流程闭环完成 |
写 PRD 时,字段表是最容易被漏掉的部分。判断一条规则有没有写清楚,标准是:
开发看完能不能不问你就写出正则和提示文案。
下面表格里的数值全部来自 config.js,改配置即改文档。
| 字段 | 必填 | 校验规则 | 触发时机 | 失败提示 |
|---|---|---|---|---|
| 账号(注册 / 登录 / 找回) | 是 |
支持手机号或邮箱: 手机号正则 (11 位,1 开头,第二位 3-9)邮箱规则: |
input 纠错 / blur 完整校验 / submit 全量校验 | 请输入正确的手机号或邮箱,手机号需为 11 位,邮箱需包含 @ 与域名 |
| 验证码(注册 / 找回) | 是 |
纯数字;Demo 固定为 ;有效期 ,发送间隔 |
blur 校验格式 / submit 全量校验 + 接口校验内容 | 验证码为空、位数不对、内容错误、已过期分别给不同提示 |
| 密码(注册 / 重置) | 是 |
长度 ; 至少包含大写字母、小写字母、数字、特殊字符中的 |
input 实时刷新强度条 / blur 校验 / submit 全量校验 | 长度不符提示当前位数;类别不足提示"当前仅 N 类" |
| 确认密码 | 是 | 必须与密码字段完全一致 | input / blur 即时比对 / submit 全量校验 | 两次输入的密码不一致,请重新确认 |
| 新密码(重置) | 是 | 在密码规则基础上,额外要求与旧密码不同 | submit 时由接口层比对旧密码 | 新密码不能与旧密码相同,请更换一个密码 |
| 协议勾选 | 是 | 必须勾选《用户协议》与《隐私政策》 | submit 时校验;勾选后立即清除警告 | 请先阅读并勾选同意《用户协议》与《隐私政策》 |
| 记住我(登录) | 否 |
勾选:登录态 ; 不勾选:仅当前会话有效 |
submit 时读取 | 无需提示;个人中心会显示当前生效方式 |
| 重置凭证 token | 是(URL 参数) | 必须存在、与本地记录一致、未过期;有效期 ;一次性使用 | 进入重置页时立即校验 + 提交时再次校验 | 缺失提示"链接不完整";无效提示"链接无效或已使用";过期提示"链接已过期" |
| 强度 | 判定条件 | 进度条颜色 | 是否允许提交 |
|---|---|---|---|
| 未输入 | 密码框为空 | 灰色(0 格) | 否 |
| 弱 | 长度不足 或字符类别少于 | 红色(1 格) | 否,需要继续输入 |
| 中 | 长度达标且满足 类字符 | 黄色(2 格) | 是 |
| 强 | 满足 4 类字符且长度 ≥ 12 位 | 绿色(3 格) | 是(推荐等级) |
这张表是账号体系 PRD 的核心。每一行都回答三个问题: 什么情况下会出问题、系统怎么拦、 拦完怎么让用户有路可走。 最后一列"可恢复性"尤其重要 —— 好的拦截一定给用户留出口。
| 流程 | 异常场景 | 错误码 | 拦截策略 | 提示方式 | 用户可恢复路径 |
|---|---|---|---|---|---|
| 通用 | 账号为空 | ACCOUNT_REQUIRED |
提交前拦下,不发请求 | inline 标红 + toast | 补填即可 |
| 账号格式错误(既不是手机号也不是邮箱) | ACCOUNT_FORMAT |
正则不通过即拦下,blur 时提前提醒 | inline 标红并说明正确格式 | 修正格式后重试 | |
| 网络异常 / 接口失败 | NETWORK_ERROR |
统一降级提示,不改变页面状态;按钮恢复可点击 | 红色 toast「网络开小差了,请检查网络后重试」 | 重试即可;可用首页开关复现 | |
| 注册 | 账号已存在 | ACCOUNT_EXISTS |
发送验证码前、提交注册前各拦一次 | inline + toast「该账号已注册,请直接登录」 | 提供「去登录」入口 |
| 验证码为空 / 格式不对 | CODE_REQUIRED / CODE_FORMAT |
提交前格式校验 | inline 提示需 6 位数字 | 补填或修正 | |
| 验证码错误 | CODE_INVALID |
接口比对失败,不消耗验证码次数 | inline + toast 提示错误 | 可重新输入;也可重新获取 | |
| 验证码已过期 | CODE_EXPIRED |
超过 即判定失效 | 黄色警告 toast,提示重新获取 | 点击「获取验证码」重新发送 | |
| 发送过于频繁 | CODE_TOO_FREQUENT |
距上次发送不足 ,按钮保持禁用 | 提示还需等待的秒数 | 倒计时结束后自动可点 | |
| 未勾选协议 / 密码不合规 | AGREEMENT_REQUIRED / PASSWORD_LENGTH / PASSWORD_STRENGTH / PASSWORD_MISMATCH |
提交前全量校验,定位到具体字段 | inline 标红 + toast 汇总数量 | 按提示逐项修正 | |
| 登录 | 账号不存在 | ACCOUNT_NOT_FOUND |
查表失败即拦下,不进入密码比对 | inline + toast「该账号未注册,请先注册」 | 提供「去注册」入口 |
| 密码错误(未达锁定阈值) | PASSWORD_WRONG |
失败计数 +1,返回剩余尝试次数 | inline 标红 + 提示「还可以尝试 N 次」 | 重新输入;忘记密码可走找回 | |
| 连续失败达到 | ACCOUNT_LOCKED |
写入锁定截止时间, 内拒绝一切登录尝试 | 顶部红色横幅 + 实时倒计时,按钮禁用 | 倒计时结束自动解锁;也可通过重置密码间接解锁 | |
| 已登录用户再次访问登录页 | — | 检测到有效登录态直接放行 | 轻提示后自动跳转个人中心 | 无需操作 | |
| 登录态过期 | SESSION_EXPIRED / NO_SESSION |
访问受保护页面时校验,失效即跳登录页 | 警告 toast + 带 redirect 跳转 | 重新登录后自动回到原页面 | |
| 找回 / 重置 | 账号未注册 | ACCOUNT_NOT_FOUND |
发送验证码前拦下 | inline + toast「该账号未注册,请先注册」 | 提供「去注册」入口 |
| 凭证缺失 / 无效 | TOKEN_MISSING / TOKEN_INVALID |
进入重置页立即校验,不渲染表单 | 整页错误态 + toast,3 秒后自动跳回找回页 | 重新验证身份获取新凭证 | |
| 凭证过期 | TOKEN_EXPIRED |
超过 即失效并清除本地记录 | 黄色警告态 + 倒计时归零时立即切换 | 重新走找回密码流程 | |
| 新密码与旧密码相同 | PASSWORD_SAME_AS_OLD |
接口层比对旧密码后拦下 | inline 标红 + toast 提示更换 | 换一个密码即可 |
文案是用户唯一能"看见"的校验逻辑。写文案最大的坑是只描述现象、不给原因、不给动作。 下面每一行都遵循「发生了什么 + 为什么 + 现在该怎么办」的三段式。
| 场景 | 文案模板(三段式) | 本项目实际文案 | 反例(不要这样写) |
|---|---|---|---|
| 账号为空 | [缺什么] + [为什么重要] + [怎么填] | 请输入手机号或邮箱 | 参数错误 / error |
| 账号格式错误 | [格式要求] + [示例] | 请输入正确的手机号或邮箱,手机号需为 11 位,邮箱需包含 @ 与域名 | 格式不正确 |
| 账号已注册 | [结论] + [替代动作] | 该账号已注册,请直接登录 | 手机号重复 |
| 账号未注册 | [结论] + [替代动作] | 该账号未注册,请先注册 | 用户不存在 |
| 验证码发送成功 | [动作完成] + [有效期] + [仍可发送次数] | 验证码已发送:123456(60 秒内有效),今日还可发送 9 次 | 发送成功 |
| 验证码错误 | [结论] + [动作] | 验证码错误,请检查后重新输入 | 验证码错误 |
| 验证码过期 | [原因] + [动作] | 验证码已过期,请重新获取(有效期 60 秒) | 失效 |
| 发送过于频繁 | [原因] + [具体还要等多久] | 发送过于频繁,请 43 秒后再试 | 请求过于频繁 |
| 当日达上限 | [结论] + [恢复时间] | 今日验证码发送次数已达上限(10 次),请明天再试 | 超出限制 |
| 密码长度不符 | [要求] + [当前情况] | 密码长度需为 8-32 位,当前 5 位 | 密码太短 |
| 密码强度不足 | [要求] + [当前情况] | 密码需至少包含大小写字母、数字、特殊字符中的 3 类,当前仅 2 类 | 密码太简单 |
| 两次密码不一致 | [结论] + [动作] | 两次输入的密码不一致,请重新确认 | 密码不匹配 |
| 新密码与旧密码相同 | [原因] + [动作] | 新密码不能与旧密码相同,请更换一个密码 | 非法密码 |
| 未勾选协议 | [动作] + [原因] | 请先阅读并勾选同意《用户协议》与《隐私政策》 | 请勾选协议 |
| 密码错误(未锁定) | [结论] + [剩余机会] + [替代方案] | 密码错误,还可以尝试 3 次(忘记密码可走找回流程) | 密码错误 |
| 账号锁定 | [状态] + [原因] + [解锁时间倒计时] | 账号已被锁定,请于 14 分 32 秒后再试 | 登录受限 |
| 重置链接过期 | [状态] + [有效期] + [动作] | 重置链接已过期(有效期 30 分钟),请重新获取 | 链接失效 |
| 网络异常 | [通俗原因] + [动作] | 网络开小差了,请检查网络后重试 | Network Error |
| 系统兜底 | [通用原因] + [动作] | 系统繁忙,请稍后重试 | 未知错误 500 |
本 Demo 的提示分了四种颜色,不是"为了好看",而是对应四种不同的用户处境。 分不清该用哪个,就会出现"红色警告刷满屏但用户根本不知道自己错在哪"的经典问题。
含义:操作已完成,且结果是用户想要的。
使用场景:
含义:操作未完成,且原因需要用户修改输入或稍后重试。
使用场景:
含义:当前操作被限制或即将失效,需要用户注意,但还没"失败"。
使用场景:
含义:纯告知,不需要用户做任何纠错动作。
使用场景:
| 问自己 | 答"是"时用什么 |
|---|---|
| 操作成功完成了吗? | 成功(绿) |
| 用户能通过修改输入自己解决吗? | 失败(红),并把错误标在具体字段下方 |
| 用户什么都做不了,只能等或重新发起流程? | 警告(黄),并给出剩余等待时间 |
| 只是告知信息,不影响任务继续? | 信息(蓝) |
| 同一个错误既有 inline 提示又有 toast,会不会太吵? | 字段错误用 inline,跨字段/流程级错误用 toast,二者不重复播报同一条 |
账号流程是典型的"漏斗"。没有埋点,你只能知道"注册量下降了",但不知道为什么。 下面是本 Demo 实际埋的事件名(可直接在代码里搜索到),以及每个事件能回答什么问题。
| 事件名 | 触发时机 | 建议携带参数 | 能回答的问题 |
|---|---|---|---|
page_view |
每次页面加载 | page | 各流程页面的流量分布与来源占比 |
register_start |
用户点击注册提交(或首次输入账号) | 账号类型(手机号/邮箱)、来源页 | 有多少人真的开始尝试注册 |
register_send_code |
点击获取验证码 | 账号(脱敏)、当日第几次 | 验证码环节的流失率与异常发送占比 |
register_success |
注册成功且已写入登录态 | 账号(脱敏)、账号类型 | 注册转化率 = 成功 / 开始 |
register_fail |
注册被拦截 | 错误码 | 哪一类错误最常把用户挡在门外 |
login_start |
点击登录 | 是否勾选记住我 | 登录发起量、免登录偏好 |
login_success |
登录成功 | 账号(脱敏)、记住我 | 登录成功率、日活回访质量 |
login_fail |
登录失败(密码错误或账号不存在) | 错误码、已失败次数 | 是否存在撞库攻击、是否存在账号枚举探测 |
login_locked |
账号被锁定 | 失败次数 | 锁定策略是否过严(高频触发说明阈值需要调整) |
logout |
主动退出登录 | 登录时长 | 会话时长分布,用于评估"记住我"天数是否合理 |
forgot_start |
进入找回密码并校验账号 | 账号(脱敏) | 忘记密码的发生频率 |
forgot_send_code |
找回流程中发送验证码 | 账号(脱敏) | 找回流程的验证码环节流失 |
reset_token_created |
身份验证通过并生成重置凭证 | 账号(脱敏) | 从"发起找回"到"验证通过"的转化率 |
reset_password_success |
密码重置成功 | 账号(脱敏) | 找回密码整体闭环率(核心指标) |
reset_password_fail |
重置被拦截 | 错误码 | 凭证过期率是否偏高(说明 30 分钟太短) |
page_view(注册页)→ register_start → register_send_code → register_success。 哪一段掉得最多,问题就在哪一段。
login_start → login_success。如果 login_fail 集中在少数账号上,基本可以判定是撞库。
forgot_start → reset_token_created → reset_password_success。 凭证过期率高就说明有效期设置不合理。
reset_token_created 的绝对量突然暴涨,往往意味着有人在批量尝试重置他人账号;
② login_fail 的错误码若大量是 ACCOUNT_NOT_FOUND,
说明有人在用手机号段扫描已注册账号,需要加验证码或风控。
下面这块显示的是你在本 Demo 里的真实行为记录,来自 localStorage 的
events。
注意:payload 里只放脱敏账号,绝不记录明文密码。
| 维度 | 本 Demo 的做法(仅供演示) | 真实生产必须怎么做 | 不做的后果 |
|---|---|---|---|
| 校验位置 | 前端校验为主,mock-api.js 再校验一次做示意 | 后端必须独立做全量校验,前端校验只作为体验优化 | 绕过页面直接调接口即可写入脏数据、越权注册 |
| 传输安全 | 无网络请求,纯本地 | 全站 HTTPS,禁止 HTTP 回退,开启 HSTS | 密码可被中间人明文窃听 |
| 密码存储 | localStorage 明文存储 | 服务端加盐哈希(bcrypt / scrypt / Argon2),永不可逆、永不落明文日志 | 一库泄露全站账号沦陷 |
| 限流与风控 | 仅本地模拟 60 秒间隔、每日 10 次、失败 5 次锁定 | 服务端按 IP / 设备 / 账号多维限流,接入风控与行为验证码,异常直接拦截 | 被刷短信(直接烧钱)、被撞库拖垮数据库 |
| 验证码 | 固定 123456,直接显示在页面上 | 服务端随机生成、加盐哈希后存储、限次校验、一次性作废;短信只下发不落库 | 固定验证码等于没有验证码 |
| 重置凭证 | 本地生成随机 token,30 分钟有效,一次性 | 服务端生成高熵随机串,绑定账号与用途,一次有效,使用后全量失效同账号其他凭证 | 凭证可预测则账号可被任意接管 |
| 会话管理 | localStorage + sessionStorage 标记模拟 | HttpOnly + Secure + SameSite Cookie 存会话票据;敏感操作二次验证 | XSS 可直接窃取登录态 |
| 账号枚举 | 分开提示"账号不存在"和"密码错误"(便于教学) | 统一提示"账号或密码错误";找回密码对未注册账号也返回相同响应 | 攻击者可批量探测出哪些手机号已注册 |
| 审计日志 | 本地埋点流水,仅用于教学演示 | 服务端记录登录、改密、重置、锁定等敏感操作日志,保留时长符合法规,可追溯 | 出事无法定位,合规审查不通过 |
| 隐私合规 | 协议勾选 + 提示"数据仅存本地" | 明示收集目的与范围、最小必要原则、单独同意、提供注销与数据导出通道 | 违反《个人信息保护法》,面临处罚与下架 |
| 敏感信息脱敏 | 埋点与展示统一使用脱敏账号(如 138****8888) | 日志、埋点、客服后台全部脱敏;禁止在埋点 payload 中出现密码类字段 | 数据泄露面被放大,内部人员可批量获取账号 |
| 前端构建 | 零依赖纯静态,无第三方脚本 | 配置 CSP、依赖锁定与漏洞扫描,避免引入被投毒的第三方资源 | 供应链攻击可导致全站被劫持 |
两边规则必须完全一致。前端做"即时反馈",后端做"最后一道门"。 在 PRD 里写校验规则时,务必写明"前后端一致,以后端为准"。
限流、锁定、过期这些机制一定会误伤正常用户。 所以必须配套:倒计时可见、过期可重新获取、锁定可自动解除、被封可找客服。
验收时最容易被漏掉的是"边界值"。经验法则是: 对每一个数值规则,都至少测 最小值 - 1、最小值、最大值、最大值 + 1 四个点。
| 编号 | 前置条件 | 操作步骤 | 预期结果 |
|---|---|---|---|
| N-01 | 无本地数据 | 注册页输入手机号 → 获取验证码 → 填 123456 → 设置合规密码 → 勾选协议 → 提交 | 提示注册成功,自动登录并跳转个人中心,显示该账号 |
| N-02 | 已有账号 A | 登录页输入 A + 正确密码 → 勾选记住我 → 登录 | 登录成功跳转个人中心;登录方式显示"账号密码登录",到期时间为 7 天后 |
| N-03 | 已登录 | 个人中心点击退出登录 → 二次确认 | 提示已安全退出,回到首页;再访问个人中心自动跳登录页 |
| N-04 | 已有账号 A | 找回密码:输入 A → 获取验证码 → 填 123456 → 提交 | 生成本地重置凭证,跳转重置页并显示脱敏账号与倒计时 |
| N-05 | 持有有效凭证 | 输入合规新密码(与旧密码不同)→ 确认 → 提交 | 提示密码已重置,跳转登录页并显示绿色引导横幅 |
| N-06 | 密码刚被重置 | 用新密码登录 | 登录成功,闭环完成;用旧密码登录应失败 |
| N-07 | 未登录 | 直接访问 dashboard.html | 跳转 login.html?redirect=dashboard.html,登录后自动回到个人中心 |
| 编号 | 操作步骤 | 预期结果 |
|---|---|---|
| E-01 | 注册时账号留空直接提交 | 账号字段标红并提示"请输入手机号或邮箱",同时 toast 汇总,不发生跳转 |
| E-02 | 输入 12345 作为手机号提交 |
提示格式错误,说明需为 11 位手机号或合法邮箱 |
| E-03 | 输入 abc@@qq 作为邮箱提交 |
提示邮箱格式不正确 |
| E-04 | 用已注册账号走注册流程并获取验证码 | 提示"该账号已注册,请直接登录",不发送验证码 |
| E-05 | 验证码输入 000000 |
提示验证码错误,验证码未被消耗,可重新输入 |
| E-06 | 发送验证码后等待 60 秒以上再提交 | 提示验证码已过期,需重新获取 |
| E-07 | 60 秒内重复点击「获取验证码」 | 按钮处于禁用状态并显示剩余秒数,无法重复发送 |
| E-08 | 同一账号当日发送验证码第 11 次 | 提示"今日验证码发送次数已达上限(10 次),请明天再试" |
| E-09 | 密码输入 Ab1!(4 位)提交 |
提示长度需 8-32 位,并显示当前位数 |
| E-10 | 密码输入 abcdefgh(仅小写,1 类)提交 |
提示需至少 3 类字符,并显示当前类别数 |
| E-11 | 密码与确认密码不一致 | 确认密码字段标红,提示两次输入不一致 |
| E-12 | 不勾选协议提交 | 提示需先阅读并勾选用户协议与隐私政策;勾选后警告自动消失 |
| E-13 | 登录不存在的账号 | 提示"该账号未注册,请先注册" |
| E-14 | 连续输错密码 1~4 次 | 每次提示"密码错误,还可以尝试 N 次",N 正确递减 |
| E-15 | 连续输错密码第 5 次 | 提示账号已锁定 15 分钟,顶部出现红色横幅并开始倒计时,提交按钮禁用 |
| E-16 | 锁定状态下刷新页面 | 横幅与倒计时依然存在且时间准确(不是从头开始) |
| E-17 | 锁定状态下即使输入正确密码 | 仍然被拒绝,提示仍在锁定中 |
| E-18 | 已登录时访问 login.html | 自动跳转个人中心,不展示登录表单 |
| E-19 | 找回密码输入未注册账号并获取验证码 | 提示"该账号未注册,请先注册",不发送验证码 |
| E-20 | 直接访问 reset-password.html(不带 token) |
显示错误态提示链接不完整,约 3 秒后自动跳回找回密码页 |
| E-21 | 访问 reset-password.html?token=伪造的 |
提示重置链接无效或已使用,自动跳回找回密码页 |
| E-22 | 重置时新密码与旧密码相同 | 提示"新密码不能与旧密码相同,请更换一个密码" |
| E-23 | 重置成功后再次使用同一个 token 链接 | 提示链接无效(凭证已被作废),需重新走找回流程 |
| E-24 | 在首页打开"模拟网络异常"开关后提交表单 | 约 10% 概率出现红色 toast「网络开小差了」,按钮恢复可点击,页面不白屏 |
| E-25 | 手动把 localStorage 中某个键改成非法 JSON 后刷新 | 页面正常渲染,坏数据被自动清理并回退默认值,控制台无报错 |
| E-26 | 尝试构造 login.html?redirect=https://evil.com |
redirect 被白名单校验拒绝,登录后跳转个人中心而非外部站点 |
| 编号 | 规则 | 边界输入 | 预期结果 |
|---|---|---|---|
| B-01 | 密码长度 8-32 位 | 7 位 / 8 位 / 32 位 / 33 位 | 7 位与 33 位被拒绝;8 位与 32 位通过(仍需满足类别要求) |
| B-02 | 密码至少 3 类字符 | 2 类 / 3 类 / 4 类 | 2 类被拒绝;3 类判定为"中";4 类且长度 ≥ 12 判定为"强" |
| B-03 | 验证码 6 位数字 | 5 位 / 6 位 / 7 位 / 含字母 | 仅 6 位纯数字通过,其余提示格式错误(输入框限制 6 位) |
| B-04 | 手机号 11 位 | 10 位 / 11 位 / 12 位 | 仅 11 位且第二位为 3-9 通过 |
| B-05 | 登录失败锁定 | 第 4 次 / 第 5 次 / 第 6 次 | 第 4 次仍可尝试;第 5 次触发锁定;第 6 次直接被锁定拦截 |
| B-06 | 锁定 15 分钟 | 14:59 / 15:00 | 14:59 仍被拦截并显示倒计时;15:00 自动解锁,按钮恢复可用 |
| B-07 | 验证码 60 秒有效期 | 59 秒 / 60 秒 / 61 秒 | 59 秒有效;60 秒起判定过期,提示重新获取 |
| B-08 | 发送间隔 60 秒 | 59 秒 / 60 秒 | 59 秒时按钮仍禁用并显示"1 秒后重发";60 秒后可点击 |
| B-09 | 每日发送上限 10 次 | 第 10 次 / 第 11 次 | 第 10 次成功并提示"今日还可发送 0 次";第 11 次被拒绝 |
| B-10 | 重置凭证 30 分钟 | 29:59 / 30:00 | 29:59 可重置;30:00 起提示过期并跳回找回页 |
| B-11 | 记住我有效期 | 勾选 / 不勾选 | 勾选为 7 天;不勾选关闭浏览器后失效(刷新页面仍有效) |
| B-12 | 超长输入 | 200 字符的邮箱 | 被格式校验拒绝或长度限制截断,页面不卡顿、不报错 |
config.js 把对应常量临时改小(例如 60000 改成 6000),
测完再改回来即可。
这七条是可以直接搬到任何表单类需求里的通用原则。每一条都标注了本 Demo 的具体落地位置, 方便你对照着看代码或页面表现。
问题:用户手抖点两下,就产生两条注册记录或两次扣费。
落地:提交时按钮立即进入 loading 并 disabled,同时设置
aria-busy="true";接口返回后统一恢复。见 ui.js 的
setButtonLoading 与 runTask。
问题:边打字边报错,用户还没输完就看到一片红,体验极差。
落地:blur(失焦)时完整校验并提示;input(输入中)只在"已经标红且现在已合规"时
立刻清除错误。既有反馈,又不打扰。见 auth-pages.js 的 bindRealtime。
问题:只弹一句"请检查输入",用户不知道看哪儿。
落地:错误文案贴在该字段下方并标红边框;提交时自动滚动并聚焦第一个出错字段; 同时用 toast 汇总"还有 N 项需要修改"。
问题:一个字段错了就清空整个表单,用户会直接关掉页面。
落地:校验失败不清空任何已填内容;验证码错误不消耗发送次数; 锁定过期自动恢复且不要求刷新页面。
问题:按钮变灰却没说明原因,用户以为坏了。
落地:验证码按钮显示"43 秒后重发",锁定横幅显示"14 分 32 秒", 提交按钮显示 spinner 与"处理中…"。所有倒计时基于绝对时间戳,刷新页面依然准确。
问题:锁定后没人解锁,用户永远回不来。
落地:15 分钟自动解锁并切换为"锁定已解除"提示;验证码过期后可重新获取; 重置链接过期后自动跳回上一步而不是卡在死页面。
问题:用户碰壁后不知道去哪,只能找客服或直接放弃。
落地:每一条失败提示背后都配了一个出口 —— "该账号已注册,请直接登录"配「去登录」;"该账号未注册,请先注册"配「去注册」; "链接已过期"配"自动回到找回密码页";锁定提示配"倒计时结束后自动恢复"。 验收时可以逐条自查:这条提示下面,用户能点的是什么?
| 检查项 | 是否写清楚 | 本 Demo 对应位置 |
|---|---|---|
| 每个字段的校验规则与失败文案 | 必写 | 本页 ④ 字段与校验规则表 |
| 每个异常的错误码与拦截时机 | 必写 | 本页 ⑤ 异常分支与拦截策略表 |
| 每个状态的进入/退出条件与界面表现 | 必写 | 本页 ③ 状态机 |
| 倒计时、有效期、锁定时间的具体数值 | 必写(不能写"一段时间") | config.js RULES 常量 |
| 提示类型(成功/失败/警告/信息)的选择依据 | 建议写 | 本页 ⑦ 四类提示的使用场景 |
| 埋点事件名与参数 | 建议写 | 本页 ⑧ 埋点建议 |
| 安全与合规要求(哈希、HTTPS、限流、审计) | 必写 | 本页 ⑨ 安全与合规说明 |
| 异常流与边界值测试用例 | 必写 | 本页 ⑩ 测试用例 |
这七个问题是产品经理新人问得最多的。每一条都对应上面某个章节,可以互相对照阅读。
倒计时同时服务三个目的:给用户明确的等待预期(避免反复点击导致重复请求); 控制成本(短信按条计费,没有任何限制会迅速烧钱); 最基础的风控(配合每日 10 次上限,显著抬高刷验证码的成本)。
数值怎么定?通常取"正常用户收到短信所需时间 + 一点余量"。 60 秒是行业长期验证过的平衡点,太短会误伤,太长则让用户焦躁。
这是一个典型的取舍。分开提示对用户更友好 —— 输错账号的人会立刻明白问题在哪; 但它同时暴露了"哪些手机号已注册",攻击者可以批量探测,属于账号枚举风险。
本 Demo 选择分开提示,是为了让你在页面上直接看到两种不同的错误码与文案。 真实生产建议统一提示"账号或密码错误",并配合登录失败计数与风控。 判断标准:业务对账号隐私的敏感程度。
锁定阈值要在"防暴力破解"和"别把真实用户挡在门外"之间取平衡。 1 次就锁,用户打错一个字母就得等 15 分钟,投诉率会飙升; 次数太多(比如 20 次)则失去防护意义。
5 次 + 15 分钟自动解锁是较常见的折中。关键指标是 LOGIN_LOCKED 埋点的触发率 —— 如果锁定频繁触发,说明阈值过严,应该调整而不是硬扛。
重置凭证的本质是"临时授权证明"。它必须同时满足四个条件: 短时效(本 Demo 为 30 分钟)、一次有效(用完立即作废)、 绑定具体账号(不能换个账号用)、不可猜测(随机生成而非递增编号)。
如果允许"输入手机号就直接改密码",那么任何人知道你的手机号就能接管你的账号。 在演示中你可以试试:重置成功后再用同一个链接访问,会提示"链接无效或已使用"。
长度是第一道防线:长度每增加一位,穷举空间成倍增长。 只要求长度的问题是用户会集中使用生日、手机号片段这类弱密码。
类别多样性用来扩大搜索空间,但只要求复杂度也容易逼出
Password1 这种模式化密码。两者结合,再配上强度条做正向引导,
是目前体验与安全比较平衡的方案。上限设 32 位是为了兼容性和防止超长输入带来的性能问题。
全部数据只写进你浏览器的 localStorage,键名前缀统一为
。整个项目是纯静态页面,
没有任何后端、数据库或网络请求,因此不存在数据外传的可能。
你可以在 首页的演示数据控制台 里 看到当前所有数据(用户表、登录态、验证码、锁定状态、重置凭证、埋点流水), 也可以一键重置。想手动清空,浏览器开发者工具的 Application → Local Storage 里删掉带前缀的键即可。
必须。前端校验只解决体验问题:用户可以绕过页面,用 Postman 之类的工具 直接调用接口,或者干脆改掉浏览器里的 JavaScript。
所以分工是:前端校验负责"即时反馈",后端校验负责"安全底线",
两端规则必须完全一致,且以后端判定为准。
本 Demo 的 mock-api.js 就是在模拟这层后端校验 ——
你会发现它在页面已经校验过一遍之后,仍然把所有规则又独立检查了一次。