当前位置: 首页 > news >正文

验证码流程

一、 整体流程概览

第一阶段:请求验证码

1. 用户请求验证码
  • 用户操作:用户在前端页面(如注册、登录、找回密码等场景)点击「获取验证码」按钮,并输入自己的邮箱地址。
  • 前端行为:前端页面捕获用户操作,向后端发送一个异步请求(如 AJAX 或 Fetch)。
    • 请求接口示例POST /api/send-code
    • 请求参数示例{ "email": "user@example.com" }
2. 后端生成验证码
  • 后端接收请求:Java 后端程序接收到 /api/send-code 请求。
  • 生成验证码
    • 生成一个随机的验证码字符串,通常为 6位数字 或 字母数字组合(例如:A1B2C3 或 483920)。
    • 同时,为该验证码设置一个 过期时间,例如 5分钟。这个时间点用于后续判断验证码是否失效。
3. 存储验证码到数据库
  • 数据库操作:后端将生成的验证码及相关信息存入数据库的 verification_code 表(或类似名称的表)中。
  • 存储字段
    • email (或 phone):用户的唯一标识,用于后续查询。
    • code:生成的验证码内容。
    • created_at / generation_time:验证码的生成时间。
    • expire_at / expiration_time:验证码的过期时间(生成时间 + 有效期)。
    • is_used:一个布尔值或状态位,标记验证码是否已被使用(默认为 false 或 0)。
4. 发送验证码
  • 调用邮件服务:后端调用邮件发送服务。
    • 可以使用 Java 内置的 JavaMailSender,也可以集成第三方邮件服务 API(如 SendGrid, Mailgun, 阿里云邮件推送等)。
  • 邮件内容:将生成的验证码嵌入到邮件模板中,发送到用户指定的邮箱地址。
  • 用户接收:用户在自己的邮箱中查收邮件,并获取到验证码。

第二阶段:校验验证码

5. 用户输入验证码
  • 用户操作:用户在前端页面的输入框中,填写从邮件中收到的验证码。
  • 前端行为:用户点击「提交」、「验证」或「注册」按钮,前端将用户输入的验证码和邮箱地址一起提交到后端。
    • 请求接口示例POST /api/verify-code
    • 请求参数示例{ "email": "user@example.com", "code": "A1B2C3" }
6. 后端验证逻辑
  • 后端接收请求:Java 后端程序接收到 /api/verify-code 请求。
  • 数据库查询:根据请求中的 email,去数据库的 verification_code 表中查询最新的、未被使用的验证码记录。
  • 核心校验步骤:后端按顺序进行以下检查,任何一步不通过则验证失败:
    1. 记录是否存在:数据库中是否存在该 email 对应的验证码记录?
    2. 是否已过期:当前系统时间是否早于记录中的 expire_at(过期时间)?
    3. 是否已使用:记录中的 is_used 字段是否为 false(未使用)?
    4. 内容是否匹配:用户输入的 code 是否与数据库中存储的 code 完全一致(注意区分大小写)?
7. 返回结果
  • 验证通过

    • 条件:以上所有校验步骤均通过。
    • 后端操作
      1. 安全最佳实践)立即将该验证码记录的 is_used 字段更新为 true,防止验证码被重复使用。
      2. 执行后续业务逻辑,如完成注册、登录或密码重置。
    • 返回结果:向前端返回成功响应。
      • 响应示例{ "status": "success", "message": "验证码正确" }
  • 验证失败

    • 条件:上述任何一个校验步骤未通过(如验证码错误、已过期、不存在或已使用)。
    • 后端操作:不执行后续业务逻辑。
    • 返回结果:向前端返回失败响应,并提示用户失败原因(或通用提示)。
      • 响应示例{ "status": "fail", "message": "验证码错误或已失效,请重新获取" }
  • 前端处理:前端根据后端返回的结果,给用户相应的提示(如跳转页面、显示成功信息、或提示错误并允许重新获取验证码)。


二、 详细步骤分解

我们将整个过程分为两大阶段:验证码发送阶段验证码校验阶段

阶段一:验证码发送阶段

目标: 生成一个唯一的、有时效性的验证码,并将其安全地发送给用户。

步骤角色动作详细说明
1. 触发请求用户 -> 前端用户操作用户在界面上(如注册、找回密码页面)输入自己的邮箱地址,并点击“获取验证码”按钮。
2. 请求后端前端 -> 后端API调用前端通过HTTP POST请求,将用户输入的邮箱地址发送到后端的一个专门接口,例如 /api/v1/auth/send-verification-code
3. 生成验证码后端业务逻辑后端接收到请求后:
1. 校验邮箱格式:首先检查邮箱地址是否合法。
2. 生成随机码:使用安全的随机数生成器(如 SecureRandom)生成一个验证码。通常为4-8位,可以是纯数字、数字+字母组合。为了安全,避免使用易混淆的字符(如0和O,1和l)。
3. 设定有效期:为验证码设置一个合理的生命周期,比如 5分钟 或 10分钟。这是安全的关键。
4. 存储验证码后端 -> 数据库数据持久化将生成的验证码及相关信息存入数据库。推荐的数据表结构如下:
verification_codes 表
id (主键, BIGINT, 自增)
email (VARCHAR, 索引) - 关联用户
code (VARCHAR) - 存储的验证码
created_at (DATETIME) - 创建时间
expires_at (DATETIME) - 过期时间 (created_at + 5分钟)
is_used (BOOLEAN, 默认false) - 是否已使用
attempts (INT, 默认0) - 尝试次数 (可选,用于防暴力破解)

重要:存储时,强烈建议不要明文存储。可以对 code 进行哈希处理(如BCrypt)后再存入数据库,这样即使数据库泄露,攻击者也无法直接获取验证码。
5. 发送邮件后端 -> 邮件服务外部服务调用后端调用邮件服务提供商的API(如SendGrid, Mailgun, Amazon SES,或公司自建邮件服务器)来发送邮件。
邮件内容要素
明确的主题:如“【XX网站】您的验证码”
清晰的正文:告知用户验证码,并提醒其有效期和用途。
安全提示:提醒用户切勿泄露验证码。
发件人地址:使用官方、可信的邮箱地址。
6. 返回结果后端 -> 前端API响应无论邮件发送是否成功,后端都应给前端一个明确的响应。
成功:返回 200 OK 或 201 Created,并附带消息如“验证码已发送”。
失败:返回 4xx 或 5xx 错误,并附带原因,如“邮箱格式不正确”、“邮件服务暂时不可用”等。
7. 前端反馈前端 -> 用户UI更新前端根据后端响应更新UI。
- 成功:按钮变为倒计时状态(如“60秒后重发”),并提示用户查收邮件。
- 失败:显示错误信息。
阶段二:验证码校验阶段

目标: 验证用户提交的验证码是否与系统发送的、且在有效期内的验证码匹配。

步骤角色动作详细说明
1. 提交验证码用户 -> 前端用户操作用户在邮件中查看到验证码,并在前端的输入框中填写,然后点击“验证”或“提交”按钮。
2. 请求校验前端 -> 后端API调用前端将用户输入的邮箱和验证码,通过HTTP POST请求发送到后端的校验接口,例如 /api/v1/auth/verify-code
3. 查询记录后端 -> 数据库数据查询后端接收到请求后,根据 email 去数据库查询最新一条未使用的验证码记录。
SQL示例SELECT code, expires_at, is_used FROM verification_codes WHERE email = ? ORDER BY created_at DESC LIMIT 1
注意:必须使用 ORDER BY created_at DESC 和 LIMIT 1 来确保我们校验的是最近发送的那一个。
4. 核对与判断后端业务逻辑这是核心的校验逻辑,需要依次进行多重判断。任何一个条件不满足,校验都应失败:
1. 记录是否存在? 如果查询不到任何记录,说明该邮箱从未请求过验证码,直接失败。
2. 是否已使用? 检查 is_used 字段。如果为 true,说明此验证码已被消费过,直接失败(防止重放攻击)。
3. 是否已过期? 检查当前时间是否大于 expires_at 字段。如果已过期,直接失败。
4. 验证码是否匹配? 将用户提交的 code 与数据库中存储的 code 进行比对。
如果明文存储:直接进行字符串比对。
如果哈希存储:使用相同的哈希算法(如BCrypt的 matches 方法)比对用户输入的明文和数据库中的哈希值。
5. 处理结果后端业务逻辑根据第4步的判断结果执行不同操作:
- 校验成功
a. 更新状态:立即将该验证码记录的 is_used 字段更新为 true,防止被再次使用。UPDATE verification_codes SET is_used = true WHERE id = ?
b. 执行后续业务:例如,将用户状态设为“已验证”,生成一个临时的访问令牌,允许用户重置密码等。
c. 返回成功响应:向前端返回 200 OK,并附带成功信息或后续操作所需的数据(如token)。

- 校验失败
a. 记录失败次数 (可选):更新该记录的 attempts 字段,如果超过阈值(如5次),可以暂时锁定该邮箱的验证请求。
b. 返回失败响应:向前端返回 400 Bad Request 或 401 Unauthorized,并在响应体中给出明确的错误原因,如“验证码错误”、“验证码已失效,请重新获取”等。注意: 不要笼统地返回“验证码错误”,这会增加被暴力破解的风险。区分“错误”和“失效”对用户更友好。
6. 前端响应前端 -> 用户UI更新前端根据后端的响应结果,引导用户进入下一步或提示错误。
- 成功:跳转到密码重置页面,或显示“账户已激活”。
- 失败:在输入框下方显示具体的错误信息。

三、 技术实现要点与最佳实践

  1. 验证码生成
    • 使用 java.security.SecureRandom 而不是 java.util.Random,因为它提供了更强的随机性,能更好地抵御预测攻击。
      import java.security.SecureRandom;public String generateCode(int length) {String chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";SecureRandom random = new SecureRandom();StringBuilder sb = new StringBuilder(length);for (int i = 0; i < length; i++) {sb.append(chars.charAt(random.nextInt(chars.length())));}return sb.toString();}
  1. 数据库设计
    • 索引:务必为 email 字段创建索引,因为查询时总是用它作为条件,能极大提升查询速度。
    • 数据清理:验证码是短期数据,应定期清理过期的、已使用的记录。可以设置一个定时任务(如使用Spring的 @Scheduled)每天凌晨清理一次,避免表无限增长。
      DELETE FROM verification_codes WHERE expires_at < NOW() OR is_used = true;
  1. 安全增强
    • 防暴力破解
      • 频率限制:对单个邮箱/IP地址,限制其在单位时间(如1分钟)内请求发送验证码的次数。
      • 尝试次数限制:在校验阶段,对单个验证码的尝试次数做限制,超过后直接使其失效。
      • 图形验证码:在“获取验证码”的按钮前,增加一个图形验证码(如reCAPTCHA),防止被恶意脚本批量刷取。
    • 防重放攻击is_used 字段是关键。一旦验证成功,必须立即将其标记为已使用,确保一个验证码只能用一次。
    • HTTPS:整个流程必须使用HTTPS协议,防止验证码在传输过程中被窃听。
    • 信息脱敏:在返回给前端的错误信息中,不要泄露过多细节。例如,当邮箱不存在时,可以返回“如果该邮箱已注册,验证码已发送”,而不是“邮箱未注册”,以避免被恶意用于探测用户信息。

四、 总结

一个健壮、安全的验证码系统是在此基础上,通过增加时效性控制状态管理安全存储防攻击策略来构建的。

核心思想总结:

  • 唯一性:一个验证码对应一次请求和一个用户。
  • 时效性:验证码必须有明确的生命周期,用完即焚。
  • 一次性:验证码一旦成功使用,必须立即失效,防止重用。
  • 安全性:在生成、存储、传输、校验的每一个环节都要考虑安全风险,并采取相应措施。
http://www.dtcms.com/a/349293.html

相关文章:

  • 【AMBA总线互联IP】
  • 6、RocketMQ消息积压问题如何解决
  • QSpinBox的用法及其使用QSS对其美化
  • 【ElasticSearch】json查询语法和可用的客户端
  • Docker 在线安装 RabbitMQ
  • 开源 C++ QT Widget 开发(五)通讯--串口调试
  • NILMTK(非侵入式负载监测工具包)安装
  • Linux 进阶之性能调优,文件管理,网络安全
  • AI精准种植改写农业格局:亩产量提升18%+水资源利用率提高32%,破解小农户技术门槛难题
  • Linux下usb设备驱动涉及的结构体
  • More Effective C++ 条款06: 区分自增自减操作符的前缀和后缀形式
  • 04-ArkTS编程语言入门
  • 分享些 Function 和 枚举的经典使用案例
  • 【RAGFlow代码详解-1】概述
  • 青少年软件编程(python六级)等级考试试卷-客观题(2023年3月)
  • 同步阻塞和异步非阻塞是什么?
  • Web开发中的CGI:通用网关接口详解
  • 软件测试用例指南:覆盖 6 大设计方法
  • 二、GP/GS流程图
  • Spring面试题及详细答案 125道(16-25) -- 核心概念与基础2
  • 工程师的自我修养
  • Linux --网络基础概念
  • 08-系统能力调用与权限管理
  • Python爬虫-解决在抓包的过程中,找不到接口地址的问题
  • ViLU: Learning Vision-Language Uncertainties for Failure Prediction
  • C++ 容器——vector
  • PyTorch入门实战:MNIST数据集加载与可视化详解
  • 一、基因组选择(GS)与基因组预测(GP)
  • 【K8s】整体认识K8s之namespace
  • OpenIM应用机器人自动应答