利用hutool工具类实现图片验证码
 # 利用hutool工具类实现图片验证码
# 一、校验验证码
提示
提供接口将收到调用端传过来的校验码,从session或redis取出验证码,两个验证码都全部转小写,进行无大小写区分匹配校验,返回true/flase 。
利用redis存储时,可以在生成验证码并发送给前端时,
同时发送这个临时的唯一标识符(比如 UUID)。当用户提交登录请求时,前端除了发送用户名、密码和验证码,还需要发送这个唯一标识符。这样,后端可以使用这个唯一标识符来从 Redis 中找到与之关联的验证码,进行验证。
# 1. 从 Session 校验
 在用户未登录时,Session 存储验证码的方式具有一些局限性和潜在问题:
- Session 与用户绑定:
Session是基于用户会话的,每个用户都有一个唯一的Session ID。在未登录状态下,浏览器首次访问应用时,服务器会创建一个Session并分配一个Session ID,这个 ID 通常通过 Cookie 保存并发送给客户端。 - 跨请求的 Session 依赖:验证码需要在不同请求之间共享,因此依赖 
Session ID的一致性。只要用户的会话有效,验证码可以一直保存在该Session中。然而,在分布式系统中,如果没有配置Session共享机制,用户可能在请求验证码和验证验证码时访问了不同的服务器,导致Session数据不同步。 - 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 2. 从 Redis 校验
 Redis 存储验证码在未登录状态下有几个明显优势:
- 分布式支持:Redis 是分布式环境下的天然解决方案,能够跨多个服务器实例共享数据,无论用户请求被分配到哪个实例,都能通过统一的 Redis 查询到验证码。
 - 灵活的键管理:在未登录状态下,验证码的存储可以通过生成一个唯一的键(如 UUID)来标识。这个键会作为响应的一部分返回给前端,前端在登录请求时将这个键与用户输入的验证码一起提交。这样,后端就可以通过这个键从 Redis 中获取对应的验证码进行验证。
 - 轻量级且独立于用户会话: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
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
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
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
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
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
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
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
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
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
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
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
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
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