图片验证码实现全解析:从生成到校验的前后端流程

[!attention] No Reproduction Without Permission
Made By Hanbin Yi

图片验证码登录交互流程

  1. 前端触发请求:用户操作(如点击 “获取验证码”)时,前端向后端发送验证码请求。
  2. 后端生成验证码
    • 生成图片验证码(含随机字符)和对应的 “正确答案”;
    • 生成唯一标识(如 sessionID),将 “唯一标识 + 验证码答案” 存储到服务端(如 Session)。
  3. 后端返回数据:把 “验证码图片 + 唯一标识” 返回给前端,前端展示验证码图片。
  4. 用户提交信息:用户输入账号密码时,同时填写验证码,并携带 “唯一标识” 一起提交登录请求。
  5. 后端校验验证码
    • 后端通过 “唯一标识” 从 Session 中取出对应的验证码答案;
    • 对比用户提交的验证码与答案,校验是否正确。

后端部分

这里需要导入一个生成图像验证码工具类
在这里插入图片描述

后端图片验证码生成接口代码

/**
 * 验证码控制器
 * 专门处理与验证码相关的HTTP请求
 */
@RestController // 标记此类为Spring MVC的控制器,并且所有方法的返回值都会自动转换为JSON格式
@RequestMapping("code") // 定义了此类中所有接口的统一访问前缀。
public class CodeController {

    /**
     * 生成并返回图片验证码的接口
     * @param session HttpSession对象,用于在服务器端存储验证码信息
     * @return ResponseDTO 自定义的响应对象,包含状态码、消息和验证码图片数据
     */
    @GetMapping() // 表示当前方法处理HTTP的GET请求
    public ResponseDTO getCode(HttpSession session) {
        // 1. 创建验证码工具类实例
        // 假设ValidateCodeUtil是一个自定义的工具类,封装了生成验证码图片和文本的逻辑
        ValidateCodeUtil validateCodeUtil = new ValidateCodeUtil();

        // 2. 生成验证码图片的Base64编码字符串
        // 该方法内部会随机生成验证码文本,并将其绘制成图片,最后编码成Base64格式的字符串
        // 这样做的好处是前端可以直接使用<img src="data:image/png;base64,xxx..."/>来显示图片,无需额外请求
        String codeImage = validateCodeUtil.getRandomCodeImage();

        // 3. 获取与图片对应的验证码文本答案
        // 从刚才生成的验证码工具类实例中获取正确的验证码字符串
        String code = validateCodeUtil.getCode();

        // 4. 生成一个唯一标识UUID
        // 这个UUID将作为该验证码在服务器端的“钥匙”,用于后续验证时查找对应的正确答案
        // 使用UUID可以确保在分布式环境或多用户并发请求时不会产生冲突
        String unique = UUID.randomUUID().toString();

        // 5. 将验证码信息存入Session
        // 以UUID为key,验证码文本为value,存储在当前用户的HttpSession中
        // 这样当用户提交登录表单时,后端就可以通过这个UUID找到之前存储的正确验证码进行比对
        session.setAttribute(unique, code);

        // 6. 构建返回给前端的数据
        // 使用HashMap来组织需要返回的数据
        HashMap<String, String> map = new HashMap<>();
        map.put("image", codeImage); // 验证码图片的Base64字符串
        map.put("unique", unique);   // 验证码的唯一标识UUID

        // 7. 返回成功响应
        // 使用自定义的ResponseDTO封装结果,返回给前端
        return ResponseDTO.success(map);
    }
  • 流程
    1. 生成随机验证码文本(如 “A7b9”)和对应的图片(转成 Base64 字符串,方便前端直接显示)。
    2. 为该验证码生成一个唯一标识(UUID)。
    3. 将 UUID 和验证码文本存入 Session(服务器端临时存储)。
    4. 返回 Base64 图片和 UUID 给前端。

在这里插入图片描述

uuid
在这里插入图片描述

用户登录验证

@Override
public ResponseDTO login(UserVO userVO, HttpSession session) {
    // 1. 从 Session 中获取存储的验证码
    //    - userVO.getUnique() 是前端传来的验证码唯一标识
    //    - session.getAttribute() 通过该标识获取之前存储的正确验证码
    //    - .toString() 确保获取到的是字符串类型
    String codeSession = session.getAttribute(userVO.getUnique()).toString();

    // 2. 校验验证码
    //    - 先判断用户是否输入了验证码 (userVO.getCode() == null)
    //    - 再比较用户输入的验证码与 Session 中存储的验证码(不区分大小写)
    //    - 如果任一条件不满足,返回验证码错误信息
    if (userVO.getCode() == null || !userVO.getCode().equalsIgnoreCase(codeSession)) {
        return ResponseDTO.fail("验证码错误");
    }

    // 3. 校验账号和密码
    //    - 调用 userMapper 的 login 方法,根据用户名和密码查询数据库
    //    - 如果返回的 userDO 为 null,说明账号或密码不匹配
    UserDO userDO = userMapper.login(userVO);
    if (userDO == null) {
        return ResponseDTO.fail("账号或密码错误");
    }

    // 4. 登录成功
    //    - 如果所有校验都通过,返回成功信息和用户数据
    return ResponseDTO.success(userDO);
}
  • 流程
    1. 从前端传入的参数中,获取 UUID 和用户输入的验证码。
    2. 用 UUID 从 Session 中取出之前存储的正确验证码,与用户输入的验证码比对(不区分大小写)。
    3. 验证码通过后,再用账号和密码查询数据库,验证身份。
    4. 所有验证通过则返回成功(带用户信息),否则返回对应错误提示(验证码错误 / 账号密码错误)。

前端部分

<template>
  <div>
    <!-- 账号输入框 -->
    账号<el-input v-model="account" placeholder="请输入账号"></el-input>
    <!-- 密码输入框,show-password 属性会显示/隐藏密码 -->
    密码<el-input v-model="password" placeholder="请输入密码" show-password></el-input>
    <!-- 验证码输入框 -->
    验证码<el-input v-model="code" placeholder="请输入验证码"></el-input>
    <!-- 验证码图片:
         - :src 绑定图片的 Base64 编码字符串
         - `data:image/png;base64,${codeImage}` 是 Data URL 格式,
           浏览器可以直接解析并显示图片,无需额外请求服务器
         - codeImage 是从后端获取的 Base64 字符串
    -->
    <img :src="`data:image/png;base64,${codeImage}`" alt="验证码" />
    <!-- 登录按钮:点击触发 login 方法 -->
    <el-button type="primary" @click="login()">登录</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      account: '',     // 绑定账号输入框
      password: '',    // 绑定密码输入框
      code: '',        // 绑定验证码输入框
      codeImage: '',   // 存储验证码图片的 Base64 字符串
      unique: ''       // 存储验证码的唯一标识 UUID
    }
  },
  methods: {
    // 登录方法
    login() {
      console.log('账号:', this.account);
      // 发送 POST 请求到 /user/login 接口
      this.$axios.post('/user/login', {
        account: this.account,    // 账号
        password: this.password,  // 密码
        code: this.code,          // 用户输入的验证码
        unique: this.unique       // 验证码的唯一标识(从后端获取)
      }).then(response => {
        // 请求成功回调
        console.log('登录成功:', response.data);
        // 在这里可以处理登录成功后的逻辑,例如:
        // 1. 存储用户信息到 localStorage 或 Vuex
        // 2. 跳转到首页或其他页面
        // this.$router.push('/home');
      }).catch(error => {
        // 请求失败回调
        console.error('登录失败:', error);
        // 在这里可以处理登录失败后的逻辑,例如:
        // 1. 显示错误提示信息(如验证码错误、账号密码错误)
        // 2. 刷新验证码图片(防止用户输入错误后无法重新获取)
        this.getCodeImage();
      });
    },

    // 获取验证码图片
    getCodeImage() {
      // 发送 GET 请求到 /code 接口
      this.$axios.get('/code')
        .then(response => {
          console.log(response);
          // 从响应数据中提取 Base64 图片和 UUID
          this.codeImage = response.data.data.image;  // 验证码图片的 Base64 字符串
          this.unique = response.data.data.unique;    // 验证码的唯一标识 UUID
          console.log(this.codeImage);
        })
        .catch(error => {
          // 获取验证码失败
          console.error('获取验证码失败:', error);
        });
    }
  },
  // 组件挂载后执行(相当于页面加载完成后)
  mounted() {
    // 自动调用 getCodeImage 方法,获取验证码图片并显示
    this.getCodeImage();
  }
}
</script>

<style></style>
  1. 加载即获取:页面加载完成后,立即向后端请求验证码图片和其唯一标识(UUID)。
  2. 展示与存储:将获取到的验证码图片(Base64 格式)显示在页面上,并隐藏存储其 UUID。
  3. 用户交互:用户输入账号、密码和验证码。
  4. 提交验证:用户点击登录时,前端将账号、密码、用户输入的验证码文本以及隐藏的 UUID 一同发送给后端。
  5. 结果处理:接收后端响应,成功则进行页面跳转等操作,失败则提示错误并刷新验证码图片。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值