程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • 后端开发

    • Spring Boot多模块项目开发
    • Spring Boot图片资源返回
    • Spring Boot文件上传
    • Spring Boot文件下载
    • 对接第三方文件上传
    • Servlet 原生API
    • HttpServletResponse 和 ResponseEntity
    • 后端解决跨域问题
    • 后端拦截器
    • SpringBoot+Vue实现邮件发送与验证码验证
    • 谷歌验证码
    • 利用hutool工具类实现图片验证码
      • 一、校验验证码
        • 1. 从 Session 校验
        • 2. 从 Redis 校验
      • 二、生成验证码并存储(支持Session和Redis)
        • 1. 项目依赖(引入 Hutool 工具类)
        • 2. 接口响应体:VerifyCodeResp
        • 3. 验证码控制器:VerifyCodeController
        • 3.1 基于 ShearCaptcha 的图片验证码(Session 存储)
        • 3.2 基于 LineCaptcha 的图片验证码(Session 存储)
        • 3.3 基于 ShearCaptcha 的 Base64 字符串验证码(Session 存储)
        • 3.4 基于 LineCaptcha 的 Base64 字符串验证码(Session 存储)
        • 3.5 基于 ShearCaptcha 的 Base64 字符串验证码(Redis 存储)
        • 3.6 基于 LineCaptcha 的 Base64 字符串验证码(Redis 存储)
      • 三、springboot+vue实现验证码
        • 1. 前端验证码输入界面
        • 2. Axios 封装:request.js
        • 3. 验证码接口:Spring Boot 实现验证码生成与存储
        • 4. 登录接口:验证用户名、密码及验证码
    • 统一返回格式
    • 通用 普通 登录模块
    • 通用 JWT 登录认证模块
    • 通用 普通 注册模块
    • 基于 MyBatis curd
    • 基于 MyBatis-Plus curd
    • Java 常见对象模型
    • 开发枚举的使用
    • MyBatis与MyBatis-Plus日期类型处理
    • 接口日志拦截基础版
    • 接口日志拦截进阶版
    • 文件操作工具类
    • Spring Boot 数据校验
    • 幂等性
  • 前端开发

  • 开发笔记
  • 后端开发
scholar
2024-12-27
目录

利用hutool工具类实现图片验证码

# 利用hutool工具类实现图片验证码

# 一、校验验证码

提示

  • 提供接口将收到调用端传过来的校验码,从session或redis取出验证码,两个验证码都全部转小写,进行无大小写区分匹配校验,返回true/flase 。

  • 利用redis存储时,可以在生成验证码并发送给前端时,同时发送这个临时的唯一标识符(比如 UUID)。当用户提交登录请求时,前端除了发送用户名、密码和验证码,还需要发送这个唯一标识符。这样,后端可以使用这个唯一标识符来从 Redis 中找到与之关联的验证码,进行验证。

# 1. 从 Session 校验

在用户未登录时,Session 存储验证码的方式具有一些局限性和潜在问题:

  1. Session 与用户绑定:Session 是基于用户会话的,每个用户都有一个唯一的 Session ID。在未登录状态下,浏览器首次访问应用时,服务器会创建一个 Session 并分配一个 Session ID,这个 ID 通常通过 Cookie 保存并发送给客户端。
  2. 跨请求的 Session 依赖:验证码需要在不同请求之间共享,因此依赖 Session ID 的一致性。只要用户的会话有效,验证码可以一直保存在该 Session 中。然而,在分布式系统中,如果没有配置 Session 共享机制,用户可能在请求验证码和验证验证码时访问了不同的服务器,导致 Session 数据不同步。
  3. Session 管理复杂性:对于多实例部署的应用,需要确保所有服务器节点能够共享 Session 数据,这通常通过负载均衡的“粘性会话”或 Session 集群管理来实现,增加了系统复杂性。
/**
 * 校验验证码(Session 存储)
 * 
 * 验证码存储方式:Session
 * 
 * @param request HttpServletRequest,用于获取Session中的验证码
 * @param inputCode String,用户输入的验证码
 * @return boolean 校验结果,true表示匹配成功,false表示匹配失败
 */
@PostMapping("/verifyCaptcha")
public boolean verifyCaptchaFromSession(HttpServletRequest request, @RequestParam String inputCode) {
    // 获取Session中的验证码
    String sessionCode = (String) request.getSession().getAttribute("verifyCode");
    if (sessionCode == null) {
        return false;
    }

    // 比较验证码(忽略大小写)
    return sessionCode.equalsIgnoreCase(inputCode);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2. 从 Redis 校验

Redis 存储验证码在未登录状态下有几个明显优势:

  1. 分布式支持:Redis 是分布式环境下的天然解决方案,能够跨多个服务器实例共享数据,无论用户请求被分配到哪个实例,都能通过统一的 Redis 查询到验证码。
  2. 灵活的键管理:在未登录状态下,验证码的存储可以通过生成一个唯一的键(如 UUID)来标识。这个键会作为响应的一部分返回给前端,前端在登录请求时将这个键与用户输入的验证码一起提交。这样,后端就可以通过这个键从 Redis 中获取对应的验证码进行验证。
  3. 轻量级且独立于用户会话:Redis 不依赖用户的会话机制,这使得验证码的存储更加灵活。在用户会话管理复杂的场景下,Redis 不需要依赖 Session ID,而是通过传递的唯一键来进行数据匹配。
/**
 * 校验验证码(Redis 存储)
 * 
 * 验证码存储方式:Redis
 * 
 * @param captchaKey String,前端传递的验证码唯一标识符
 * @param inputCode String,用户输入的验证码
 * @return boolean 校验结果,true表示匹配成功,false表示匹配失败
 */
@PostMapping("/verifyCaptchaRedis")
public boolean verifyCaptchaFromRedis(@RequestParam String captchaKey, @RequestParam String inputCode) {
    // 从Redis中获取验证码
    String redisCode = redisTemplate.opsForValue().get("captcha:" + captchaKey);
    if (redisCode == null) {
        return false;
    }

    // 比较验证码(忽略大小写)
    return redisCode.equalsIgnoreCase(inputCode);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

重要提示

  • Redis 存储:推荐在分布式环境下使用 Redis 来存储验证码,确保所有服务实例能共享同一验证码信息。
  • Base64 编码:Base64 图片字符串适合用于前端展示,减少传输复杂度。
  • 验证码失效时间:设置合理的验证码失效时间,通常建议在 5-30 分钟之间。

# 二、生成验证码并存储(支持Session和Redis)

提供接口将生成的验证码存入session或redis,将验证码以图片格式或者base64编码串返回给调用端。

# 1. 项目依赖(引入 Hutool 工具类)

在 pom.xml 中引入 Hutool 依赖:

<!-- Hutool工具类依赖 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.12</version>
</dependency>
1
2
3
4
5
6

# 2. 接口响应体:VerifyCodeResp

这个类用于封装验证码响应的格式,包括验证码的唯一标识符(如 captchaKey)和验证码图片的 Base64 编码:

@Data
public class VerifyCodeResp implements Serializable {
    /**
     * Captcha唯一标识符,前端通过此Key与Redis中的验证码匹配
     */
    private String captchaKey;

    /**
     * 验证码图片(Base64编码)
     */
    private String captchaImg;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3. 验证码控制器:VerifyCodeController

控制器类包含了多种生成验证码的方法,支持 Session 和 Redis 两种存储方式,提供多种返回格式(如图片或 Base64 字符串)。

# 3.1 基于 ShearCaptcha 的图片验证码(Session 存储)

@RestController
public class VerifyCodeController {
    
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 生成验证码并存入Session
     * 
     * 验证码类型:ShearCaptcha(扭曲验证码)
     * 存储方式:Session
     * 返回格式:JPEG 图片格式,直接输出到响应流
     * 
     * @param request HttpServletRequest,用于操作Session
     * @param response HttpServletResponse,用于返回图片数据
     * @throws IOException 当图片写入失败时抛出
     */
    @GetMapping("/verify")
    public void generateCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 设置响应格式为图片
        response.setContentType("image/jpeg");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        // 创建扭曲验证码,设置宽150,高40,字符数5,干扰线宽度4
        ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(150, 40, 5, 4);

        // 将验证码写入响应流
        shearCaptcha.write(response.getOutputStream());

        // 将验证码文字存入Session
        request.getSession().setAttribute("verifyCode", shearCaptcha.getCode());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

关键点:

  • 验证码类型:ShearCaptcha,验证码字符经过扭曲处理。
  • 存储方式:Session,验证码存储在服务端的会话中。
  • 返回方式:直接以 JPEG 图片格式返回。

# 3.2 基于 LineCaptcha 的图片验证码(Session 存储)

/**
 * 生成验证码并存入Session
 * 
 * 验证码类型:LineCaptcha(带干扰线条的验证码)
 * 存储方式:Session
 * 返回格式:JPEG 图片格式,直接输出到响应流
 * 
 * @param request HttpServletRequest,用于操作Session
 * @param response HttpServletResponse,用于返回图片数据
 * @throws IOException 当图片写入失败时抛出
 */
@GetMapping("/verifyTwo")
public void generateLineCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 设置响应格式为图片
    response.setContentType("image/jpeg");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setDateHeader("Expires", 0);

    // 创建线条验证码,设置宽150,高40,字符数5,干扰线宽度4
    LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(150, 40, 5, 4);

    // 将验证码写入响应流
    ImageIO.write(lineCaptcha.getImage(), "JPEG", response.getOutputStream());

    // 将验证码文字存入Session
    request.getSession().setAttribute("verifyCode", lineCaptcha.getCode());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

关键点:

  • 验证码类型:LineCaptcha,验证码带有干扰线条。
  • 存储方式:Session,和方法一类似。
  • 返回方式:以 JPEG 图片格式返回。

# 3.3 基于 ShearCaptcha 的 Base64 字符串验证码(Session 存储)

/**
 * 生成验证码并返回Base64编码的字符串
 * 
 * 验证码类型:ShearCaptcha(扭曲验证码)
 * 存储方式:Session
 * 返回格式:Base64 字符串
 * 
 * @param request HttpServletRequest,用于操作Session
 * @return String Base64 编码的图片字符串
 */
@GetMapping("/getVerify")
public String generateBase64Captcha(HttpServletRequest request) {
    // 创建扭曲验证码,设置宽150,高40,字符数5,干扰线宽度4
    ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(150, 40, 5, 4);

    // 将验证码文字存入Session
    request.getSession().setAttribute("verifyCode", shearCaptcha.getCode());

    // 将图片转换为Base64编码字符串返回
    return "data:image/png;base64," + shearCaptcha.getImageBase64();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

关键点:

  • 返回方式:Base64 编码的字符串,适合在前端直接展示。

# 3.4 基于 LineCaptcha 的 Base64 字符串验证码(Session 存储)

/**
 * 生成验证码并返回Base64编码的字符串
 * 
 * 验证码类型:LineCaptcha(带干扰线条的验证码)
 * 存储方式:Session
 * 返回格式:Base64 字符串
 * 
 * @param request HttpServletRequest,用于操作Session
 * @return String Base64 编码的图片字符串
 */
@GetMapping("/getVerifyTwo")
public String generateLineCaptchaBase64(HttpServletRequest request) {
    LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(150, 40, 5, 4);

    // 将验证码文字存入Session
    request.getSession().setAttribute("verifyCode", lineCaptcha.getCode());

    // 转换为Base64字符串并返回
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
        ImageIO.write(lineCaptcha.getImage(), "JPEG", bos);
        return "data:image/png;base64," + Base64.getEncoder().encodeToString(bos.toByteArray());
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 3.5 基于 ShearCaptcha 的 Base64 字符串验证码(Redis 存储)

/**
 * 生成验证码并存入Redis
 * 
 * 验证码类型:ShearCaptcha(扭曲验证码)
 * 存储方式:Redis(使用UUID作为唯一标识符)
 * 返回格式:Base64 字符串
 * 
 * @return VerifyCodeResp 响应体,包含验证码标识符和图片的Base64字符串
 */
@GetMapping("/getVerifyThree")
public VerifyCodeResp generateRedisCaptcha() {
    // 生成UUID作为验证码的唯一标识符
    String captchaKey = UUID.randomUUID().toString();
    // 创建扭曲验证码,设置宽150,高40,字符数5,干扰线宽度0
    ShearCaptcha shearCaptcha = CaptchaUtil.createShearCaptcha(150, 40, 5, 0);

    // 存入Redis,设置30分钟过期时间
    redisTemplate.opsForValue().set("captcha:" + captchaKey, shearCaptcha.getCode(), 30, TimeUnit.MINUTES);

    // 构造返回体
    VerifyCodeResp verifyCodeResp = new VerifyCodeResp();
    verifyCodeResp.setCaptchaKey(captchaKey);
    verifyCodeResp.setCaptchaImg("data:image/png;base64," + shearCaptcha.getImageBase64());

    return verifyCodeResp;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

关键点:

  • 使用 Redis 存储验证码,并通过 UUID 作为唯一标识符,适合分布式环境。
  • 返回的 VerifyCodeResp 包含验证码的 captchaKey 和 Base64 编码的图片。

# 3.6 基于 LineCaptcha 的 Base64 字符串验证码(Redis 存储)

/**
 * 生成验证码并存入Redis
 * 
 * 验证码类型:LineCaptcha(带干扰线条的验证码)
 * 存储方式:Redis(使用UUID作为唯一标识符)
 * 返回格式:Base64 字符串
 * 
 * @return VerifyCodeResp 响应体,包含验证码标识符和图片的Base

64字符串
 */
@GetMapping("/getVerifyFour")
public VerifyCodeResp generateRedisLineCaptcha() {
    // 生成UUID作为验证码的唯一标识符
    String captchaKey = UUID.randomUUID().toString();
    // 创建线条验证码,设置宽150,高40,字符数5,干扰线宽度4
    LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(150, 40, 5, 4);

    // 存入Redis,设置30分钟过期时间
    redisTemplate.opsForValue().set("captcha:" + captchaKey, lineCaptcha.getCode(), 30, TimeUnit.MINUTES);

    // 将图片转换为Base64编码字符串返回
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
        ImageIO.write(lineCaptcha.getImage(), "JPEG", bos);
        String base64String = Base64.getEncoder().encodeToString(bos.toByteArray());

        VerifyCodeResp verifyCodeResp = new VerifyCodeResp();
        verifyCodeResp.setCaptchaKey(captchaKey);
        verifyCodeResp.setCaptchaImg("data:image/png;base64," + base64String);

        return verifyCodeResp;
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 三、springboot+vue实现验证码

# 1. 前端验证码输入界面

在前端部分,通过 Vue 和 Element UI 进行表单开发,实现验证码输入功能。

<template>
  <div>
    <!-- 登录界面容器 -->
    <div
      style="width: 400px; height: 400px; margin: 150px auto; background-color:rgba(107,149,224,0.5); border-radius: 10px"
    >
      <!-- 登录标题 -->
      <div
        style="width: 100%; height: 100px; font-size: 30px; line-height: 100px; text-align: center; color: #4a5ed0"
      >
        欢迎登录
      </div>
      <!-- 登录表单 -->
      <div style="margin-top: 25px; text-align: center; height: 320px;">
        <el-form :model="admin">
          <!-- 用户名输入框 -->
          <el-form-item>
            <el-input
              v-model="admin.name"
              prefix-icon="el-icon-user"
              style="width: 80%"
              placeholder="请输入用户名"
            ></el-input>
          </el-form-item>
          <!-- 密码输入框 -->
          <el-form-item>
            <el-input
              v-model="admin.password"
              show-password
              prefix-icon="el-icon-lock"
              style="width: 80%"
              placeholder="请输入密码"
            ></el-input>
          </el-form-item>
          <!-- 验证码输入框与验证码图片 -->
          <el-form-item>
            <div style="display: flex; justify-content: center">
              <el-input
                v-model="admin.vercode"
                prefix-icon="el-icon-user"
                style="width: 42%; margin-right: 10px"
                placeholder="请输入验证码"
              ></el-input>
              <!-- 点击验证码图片进行刷新 -->
              <img :src="imgUrl" width="140px" height="33px" alt="" @click="refresh()" />
            </div>
          </el-form-item>
          <!-- 登录与注册按钮 -->
          <el-form-item>
            <el-button
              style="width: 80%; margin-top: 10px"
              type="primary"
              @click="login()"
              >登录</el-button
            >
            <el-button
              style="width: 80%; margin-top: 10px; margin-right: 10px"
              type="primary"
              @click="register()"
              >前往注册</el-button
            >
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

<script>
import request from "@/utils/request";

export default {
  data() {
    return {
      admin: {}, // 表单数据对象
      imgUrl: '' // 验证码图片的URL
    };
  },
  created() {
    // 页面加载时,自动刷新验证码
    this.refresh();
  },
  methods: {
    // 登录方法,发起登录请求
    login() {
      request.post("/admin/login", this.admin).then((res) => {
        if (res.code === "0") {
          // 登录成功处理
          this.$message.success("登录成功");
          localStorage.setItem("user", JSON.stringify(res.data));
          this.$router.push("/home");
        } else {
          // 登录失败处理
          this.$message.error(res.msg);
          this.refresh();
          this.admin.vercode = ""; // 清空验证码输入框
        }
      });
    },
    // 跳转到注册页面
    register() {
      this.$router.push("/register");
    },
    // 刷新验证码方法
    refresh() {
      request.post("/admin/code").then((res) => {
        if (res.code === "0") {
          // 更新验证码图片与唯一标识符
          this.imgUrl = res.data.captchaImg;
           // 将后端返回的该图片验证码的唯一key存进当前表单里面,用于提交到后端去获取redis里面的验证码 
          this.admin.captchaKey = res.data.captchaKey;
        }
      });
    }
  }
};
</script>

<style scoped>
/* 可以在此处编写组件的样式 */
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121

关键点:

  • 通过 v-model 实现表单数据的双向绑定。
  • 验证码图片点击后刷新,重新获取验证码。
  • 登录请求中将用户名、密码和验证码一起提交,确保输入完整。

# 2. Axios 封装:request.js

在前端通过 Axios 进行 HTTP 请求,包含请求和响应的拦截器,便于统一处理。

import axios from "axios";
import router from "@/router";

// 创建axios实例,设置基础配置
const request = axios.create({
  baseURL: "http://localhost:8082/api", // 后端服务的基础路径
  timeout: 10000 // 请求超时时间
});

// request 拦截器:发送请求前进行处理,如统一设置 token
request.interceptors.request.use(
  (config) => {
    // 设置请求头类型
    config.headers["Content-Type"] = "application/json;charset=utf-8";

    // 从本地存储中获取 token 并添加到请求头中
    const user = localStorage.getItem("user");
    if (user) {
      config.headers["token"] = JSON.parse(user).token;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// response 拦截器:响应后统一处理,如错误处理或数据解析
request.interceptors.response.use(
  (response) => {
    let res = response.data;

    // 如果后端返回的是字符串数据,进行 JSON 解析
    if (typeof res === "string") {
      res = res ? JSON.parse(res) : res;
    }
    return res;
  },
  (error) => {
    console.error("Error:", error); // 错误日志
    if (error && error.code === "401") {
      // 如果错误码为401,跳转到登录页面
      router.push("/login");
    }
    return Promise.reject(error);
  }
);

export default request;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

关键点:

  • request 拦截器中自动添加 token 以实现用户身份验证。
  • response 拦截器中统一处理后端返回数据,确保格式一致。

# 3. 验证码接口:Spring Boot 实现验证码生成与存储

在后端通过 Spring Boot 提供验证码生成接口,使用 Hutool 工具库生成带有干扰线的验证码,并将验证码存储在 Redis 中。

@RestController
@RequestMapping("/admin")
public class VerifyCodeController {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 获取图片验证码和唯一标识符
     * 
     * @return Result 封装了验证码图片的Base64字符串与唯一标识符
     * @throws IOException 当生成图片验证码失败时抛出异常
     */
    @PostMapping("/code")
    public Result getImageCode() throws IOException {
        // 使用 Hutool 创建圆形干扰线验证码,宽100像素,高40像素,字符数4,干扰线数5
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(100, 40, 4, 5);
        String code = captcha.getCode(); // 获取验证码字符
        String captchaKey = UUID.randomUUID().toString(); // 生成唯一标识符

        // 将验证码存入 Redis,设置有效期为30分钟
        redisTemplate.opsForValue().set("captcha:" + captchaKey, code, 30L, TimeUnit.MINUTES);

        // 将图片转换为Base64编码字符串
        String base64String = "data:image/png;base64," + captcha.getImageBase64();

        // 封装响应数据
        VerifyCodeResp verifyCodeResp = new VerifyCodeResp();
        verifyCodeResp.setCaptchaKey(captchaKey);
        verifyCodeResp.setCaptchaImg(base64String);

        // 返回成功响应
        return Result.success(verifyCodeResp);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

关键点:

  • 生成的验证码使用 CircleCaptcha,支持圆形干扰线。
  • 验证码存储在 Redis 中,使用 UUID 作为键,确保唯一性。
  • 将验证码图片转换为 Base64 字符串,便于前端展示。

# 4. 登录接口:验证用户名、密码及验证码

在后端登录接口中验证用户名、密码和验证码的正确性,支持 Redis 验证。

@RestController
@RequestMapping("/admin")
public class LoginController {

    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private AdminService adminService;

    /**
     * 登录接口
     * 
     * @param admin 前端传递的用户登录信息,包括用户名、密码、验证码等
     * @return Result 登录结果,包括用户信息和token
     */
    @PostMapping("/login")
    @AutoLog("登录该系统") // 自动日志记录
    public Result login(@RequestBody Admin admin) {
        // 校验用户名和密码是否为空
        if (admin.getName() == null || "".equals(admin.getName())) {
            throw new CustomException("用户名不能为空!");
        }
        if (admin.getPassword() == null || "".equals(admin.getPassword())) {
            throw new CustomException("密码不能为空!");
        }

        // 根据用户名查找用户信息
        Admin

 user = adminService.searchByName(admin.getName());
        if (user == null) {
            throw new CustomException("用户名或者密码错误!");
        }
        if (!user.getPassword().equals(admin.getPassword())) {
            throw new CustomException("用户名或者密码错误!");
        }

        // 从 Redis 中获取验证码并进行校验
        String redisCode = redisTemplate.opsForValue().get("captcha:" + admin.getCaptchaKey());
        if (redisCode == null || !admin.getVercode().equalsIgnoreCase(redisCode)) {
            throw new CustomException("验证码错误,请重新输入");
        }

        // 生成登录 token 并返回
        String token = JwtTokenUtils.genToken(String.valueOf(user.getId()), user.getPassword());
        user.setToken(token);
        user.setPassword(""); // 隐藏密码
        return Result.success(user);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

关键点:

  • 先校验用户名和密码,再校验验证码,确保完整的登录流程。
  • 验证码校验忽略大小写,使用 Redis 进行存储与验证。
编辑此页 (opens new window)
上次更新: 2025/03/16, 22:19:39
谷歌验证码
统一返回格式

← 谷歌验证码 统一返回格式→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式