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

基于Hutool的验证码功能完整技术文档

📋 目录

  1. 功能概述
  2. 技术架构
  3. 环境依赖
  4. 代码结构分析
  5. 后端实现详解
  6. 前端实现详解
  7. 完整实现步骤
  8. 配置说明
  9. API接口文档
  10. 常见问题解决
  11. 扩展建议

🎯 功能概述

本项目实现了一个完整的图形验证码功能,具备以下特性:

核心功能

  • 验证码生成:使用Hutool工具类生成图形验证码
  • 验证码验证:支持用户输入验证,验证后自动失效
  • 验证码刷新:支持手动刷新获取新验证码
  • 自定义参数:支持自定义验证码宽度、高度、字符数、干扰线数量

技术特点

  • 安全性:验证码ID与答案分离存储,前端无法获取答案
  • 时效性:验证码具有过期时间(默认5分钟)
  • 易用性:前端组件化,开箱即用
  • 响应式:支持移动端和PC端适配

🏗️ 技术架构

┌─────────────────┐    HTTP请求    ┌─────────────────┐
│   前端 Vue3     │ ──────────────→ │   后端 Spring   │
│                 │                 │                 │
│ - 验证码组件    │ ←────────────── │ - Controller    │
│ - 登录页面      │   JSON响应      │ - Service       │
│ - 状态管理      │                 │ - Cache         │
└─────────────────┘                 └─────────────────┘│                                   ││                                   │▼                                   ▼
┌─────────────────┐                 ┌─────────────────┐
│  浏览器存储     │                 │   内存缓存      │
│                 │                 │                 │
│ - 验证码图片    │                 │ - 验证码答案    │
│ - 验证码ID      │                 │ - 过期时间      │
└─────────────────┘                 └─────────────────┘

核心组件说明

  • 前端组件:Vue3 + Composition API + Tailwind CSS
  • 后端服务:Spring Boot + Hutool工具类
  • 缓存机制:内存缓存(可扩展为Redis)
  • 数据传输:RESTful API + JSON格式

🛠️ 环境依赖

后端依赖

<!-- Spring Boot Starter Web -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><!-- Hutool工具类 -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.22</version>
</dependency><!-- 日志依赖 -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId>
</dependency>

前端依赖

{"vue": "^3.3.0","vue-router": "^4.2.0","tailwindcss": "^3.3.0","naive-ui": "^2.34.0"
}

📁 代码结构分析

后端代码结构

cn.jbolt.admin.user/
├── controller/
│   └── CaptchaController.java      # 验证码控制器
├── service/
│   ├── CaptchaService.java         # 验证码服务接口
│   └── impl/
│       └── CaptchaServiceImpl.java # 验证码服务实现
└── util/└── cache/└── CaptchaCache.java       # 验证码缓存工具类

前端代码结构

src/
├── components/
│   └── verificationCode.vue       # 验证码组件
├── views/
│   └── login/
│       └── index.vue              # 登录页面
└── stores/└── user.js                    # 用户状态管理

⚙️ 后端实现详解

1. 控制器层(CaptchaController.java)

@RestController
@RequestMapping("/captcha")
@Uncheck  // 免登录验证注解
public class CaptchaController {@Autowiredprivate CaptchaService captchaService;// 生成验证码(使用默认参数)@GetMapping("/generate")public Result generateCaptcha() {return captchaService.generateCaptcha();}// 生成自定义验证码@GetMapping("/generate/custom")public Result generateCustomCaptcha(@RequestParam(defaultValue = "200") int width,@RequestParam(defaultValue = "100") int height,@RequestParam(defaultValue = "4") int codeCount,@RequestParam(defaultValue = "20") int lineCount) {return captchaService.generateCaptcha(width, height, codeCount, lineCount);}// 验证验证码@PostMapping("/verify")public Result verifyCaptcha(@RequestParam String captchaId, @RequestParam String code) {return captchaService.verifyCaptcha(captchaId, code);}// 刷新验证码@GetMapping("/refresh")public Result refreshCaptcha() {return captchaService.refreshCaptcha();}
}
关键点说明:
  • @Uncheck:确保验证码接口无需登录即可访问
  • 参数验证:使用@RequestParam设置默认值
  • 统一返回:使用Result包装返回结果

2. 服务层(CaptchaServiceImpl.java)

@Service
public class CaptchaServiceImpl implements CaptchaService {private static final Logger logger = LoggerFactory.getLogger(CaptchaServiceImpl.class);// 默认配置常量private static final int DEFAULT_WIDTH = 200;private static final int DEFAULT_HEIGHT = 100;private static final int DEFAULT_CODE_COUNT = 4;private static final int DEFAULT_LINE_COUNT = 20;private static final int DEFAULT_EXPIRE_MINUTES = 5;@Overridepublic Result generateCaptcha(int width, int height, int codeCount, int lineCount) {try {// 1. 使用Hutool生成验证码LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(width, height, codeCount, lineCount);// 2. 生成唯一IDString captchaId = IdUtil.simpleUUID();// 3. 获取验证码答案和图片String code = lineCaptcha.getCode();String imageBase64 = lineCaptcha.getImageBase64Data();// 4. 存入缓存boolean cached = CaptchaCache.putCaptcha(captchaId, code, DEFAULT_EXPIRE_MINUTES, TimeUnit.MINUTES);if (!cached) {logger.error("验证码缓存失败: captchaId={}", captchaId);return Result.error("验证码生成失败,请重试");}// 5. 构建返回结果(不包含答案)Map<String, Object> result = new HashMap<>();result.put("captchaId", captchaId);result.put("captchaImage", imageBase64);logger.info("验证码生成成功: captchaId={}", captchaId);return Result.success(result, "验证码生成成功");} catch (Exception e) {logger.error("生成验证码失败", e);return Result.error("验证码生成失败,请重试");}}@Overridepublic Result verifyCaptcha(String captchaId, String inputCode) {// 1. 参数校验if (StrUtil.isBlank(captchaId)) {return Result.error("验证码ID不能为空");}if (StrUtil.isBlank(inputCode)) {return Result.error("验证码不能为空");}try {// 2. 验证验证码(验证成功后自动移除)boolean isValid = CaptchaCache.verifyCaptcha(captchaId, inputCode.trim(), true);if (isValid) {logger.info("验证码验证成功: captchaId={}", captchaId);return Result.success(true, "验证码验证成功");} else {logger.warn("验证码验证失败: captchaId={}, inputCode={}", captchaId, inputCode);return Result.error("验证码错误或已过期");}} catch (Exception e) {logger.error("验证验证码时发生异常: captchaId={}, inputCode={}", captchaId, inputCode, e);return Result.error("验证码验证失败,请重试");}}
}
核心技术点:
  1. Hutool验证码生成CaptchaUtil.createLineCaptcha()
  2. UUID生成IdUtil.simpleUUID()确保ID唯一性
  3. Base64编码getImageBase64Data()获取图片数据
  4. 缓存管理:自定义缓存类管理验证码生命周期
  5. 安全设计:返回结果不包含验证码答案

3. 缓存管理(CaptchaCache.java)

public class CaptchaCache {private static final Map<String, CaptchaInfo> CACHE = new ConcurrentHashMap<>();// 验证码信息内部类private static class CaptchaInfo {private final String code;private final long expireTime;public CaptchaInfo(String code, long expireTime) {this.code = code;this.expireTime = expireTime;}public boolean isExpired() {return System.currentTimeMillis() > expireTime;}}// 存储验证码public static boolean putCaptcha(String captchaId, String code, int timeout, TimeUnit timeUnit) {long expireTime = System.currentTimeMillis() + timeUnit.toMillis(timeout);CACHE.put(captchaId, new CaptchaInfo(code, expireTime));return true;}// 验证验证码public static boolean verifyCaptcha(String captchaId, String inputCode, boolean removeAfterVerify) {CaptchaInfo info = CACHE.get(captchaId);if (info == null || info.isExpired()) {CACHE.remove(captchaId); // 清理过期数据return false;}boolean isValid = info.code.equalsIgnoreCase(inputCode);if (removeAfterVerify) {CACHE.remove(captchaId); // 验证后移除}return isValid;}
}

🎨 前端实现详解

1. 验证码组件(verificationCode.vue)

<template><div><div class="flex space-x-4"><!-- 验证码输入框 --><div class="flex-1"><div class="relative"><!-- 图标 --><div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none z-10"><svg class="h-5 w-5" :class="iconClass" fill="currentColor"><!-- SVG路径 --></svg></div><!-- 输入框 --><inputid="captcha":value="modelValue"@input="$emit('update:modelValue', $event.target.value)"type="text"class="input-class":class="inputClass"placeholder="验证码"@focus="onFocus"@blur="onBlur"@mouseenter="onMouseEnter"@mouseleave="onMouseLeave"></div></div><!-- 验证码图片显示区域 --><divclass="w-32 h-12 relative overflow-hidden rounded-lg cursor-pointer"@click="refreshCaptcha":class="{ 'animate-pulse': loading || isFocused }"><!-- 加载状态 --><div v-if="loading" class="loading-spinner"><svg class="animate-spin h-6 w-6 text-gray-400"><!-- 加载动画SVG --></svg></div><!-- 验证码图片 --><imgv-else-if="captchaImage":src="captchaImage"alt="验证码"class="absolute inset-0 w-full h-full object-cover"@error="onImageError"/><!-- 默认占位符 --><div v-else class="placeholder-content">点击获取</div><!-- 刷新提示 --><div class="refresh-overlay">点击刷新</div></div></div></div>
</template><script>
import { computed } from 'vue';export default {name: 'CaptchaComponent',props: {modelValue: String,      // 输入值error: String,           // 错误信息captchaImage: String,    // 验证码图片loading: Boolean,        // 加载状态focusField: String,      // 焦点字段hoverField: String       // 悬停字段},emits: ['update:modelValue', 'refresh', 'focus', 'blur', 'mouseenter', 'mouseleave'],setup(props, { emit }) {// 计算属性const isFocused = computed(() => props.focusField === 'captcha');const isHovered = computed(() => props.hoverField === 'captcha');const iconClass = computed(() => [props.error ? 'text-red-500' : (isFocused.value ? 'text-green-500' : 'text-gray-400')]);const inputClass = computed(() => [props.error ? 'ring-red-500 focus:ring-red-500' :(isFocused.value ? 'shadow-lg ring-[#4ab27d] ring-2' :(isHovered.value ? 'ring-[#4ab27d]/50' : 'ring-gray-200/70'))]);// 事件处理const onFocus = () => emit('focus');const onBlur = () => emit('blur');const onMouseEnter = () => emit('mouseenter');const onMouseLeave = () => emit('mouseleave');const refreshCaptcha = () => emit('refresh');const onImageError = () => {console.warn('验证码图片加载失败');emit('refresh'); // 自动重新获取};return {isFocused,isHovered,iconClass,inputClass,onFocus,onBlur,onMouseEnter,onMouseLeave,refreshCaptcha,onImageError};}
}
</script>
组件特点:
  1. 响应式设计:支持焦点、悬停状态变化
  2. 错误处理:自动重试加载失败的验证码
  3. 用户体验:点击图片刷新,加载动画提示
  4. 样式动态:根据状态动态调整样式类

2. 登录页面集成(index.vue)

<script setup>
import { ref, reactive, onMounted, watch } from 'vue';
import { JBoltApi } from "@/service/request/index.js";
import CaptchaComponent from './cnps/verificationCode.vue';// 验证码相关状态
const captcha = reactive({id: '',           // 验证码IDimage: '',        // 验证码图片Base64loading: false    // 验证码加载状态
});// 系统配置
const config = reactive({validateCodeEnable: false,  // 是否启用验证码
});// 获取验证码
function loadCaptcha() {if (!config.validateCodeEnable) return;captcha.loading = true;JBoltApi.tryGet('/captcha/generate').then((res) => {captcha.id = res.data.captchaId;captcha.image = res.data.captchaImage;console.debug('验证码加载成功:', captcha.id);}).catch((error) => {console.error('获取验证码失败:', error);message.error('获取验证码失败,请重试');}).finally(() => {captcha.loading = false;});
}// 刷新验证码
function refreshCaptcha() {if (!config.validateCodeEnable) return;captcha.loading = true;form.value.captcha = ''; // 清空输入errors.captcha = '';     // 清空错误JBoltApi.tryGet('/captcha/refresh').then((res) => {captcha.id = res.data.captchaId;captcha.image = res.data.captchaImage;console.debug('验证码刷新成功:', captcha.id);}).catch((error) => {console.error('刷新验证码失败:', error);message.error('刷新验证码失败');}).finally(() => {captcha.loading = false;});
}// 获取系统配置
function loadConfig() {JBoltApi.tryGet('/admin/globalConfig/getByKeys?keys=jaOLT_LOGIN_USE_CAPTURE').then((res) => {config.validateCodeEnable = res.data.jaOLT_LOGIN_USE_CAPTURE;// 如果开启了验证码,则加载验证码if (config.validateCodeEnable) {loadCaptcha();}}).catch((error) => {console.error('获取系统配置失败:', error);// 使用默认配置config.validateCodeEnable = true;loadCaptcha();});
}// 登录处理
function handleLogin() {if (!validateForm()) return;loading.value = true;const loginData = {userName: form.value.username,password: processPassword(form.value.password),};// 如果开启了验证码,添加验证码信息if (config.validateCodeEnable) {loginData.captchaCode = form.value.captcha;loginData.captchaId = captcha.id;}JBoltApi.tryPost("/auth/login", loginData).then((res) => {// 登录成功处理auth.login(res.data.user, res.data.token);router.push("/jboltai");}).catch((error) => {console.error('登录失败:', error);// 登录失败时刷新验证码if (config.validateCodeEnable) {refreshCaptcha();}}).finally(() => {loading.value = false;});
}// 生命周期
onMounted(() => {loadConfig();
});// 监听配置变化
watch(() => config.validateCodeEnable, (enabled) => {if (enabled && !captcha.image && !captcha.loading) {loadCaptcha();}
});
</script><template><!-- 验证码组件使用 --><captcha-componentv-if="config.validateCodeEnable"v-model="form.captcha":error="errors.captcha":captcha-image="captcha.image":loading="captcha.loading":focus-field="focusField":hover-field="hoverField"@refresh="refreshCaptcha"@focus="focusField = 'captcha'"@blur="focusField = ''"@mouseenter="hoverField = 'captcha'"@mouseleave="hoverField = ''"/>
</template>

🚀 完整实现步骤

步骤1:创建后端缓存工具类

// src/main/java/cn/jbolt/util/cache/CaptchaCache.java
package cn.jbolt.util.cache;import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;public class CaptchaCache {private static final Map<String, CaptchaInfo> CACHE = new ConcurrentHashMap<>();private static class CaptchaInfo {private final String code;private final long expireTime;public CaptchaInfo(String code, long expireTime) {this.code = code;this.expireTime = expireTime;}public boolean isExpired() {return System.currentTimeMillis() > expireTime;}public String getCode() {return code;}}/*** 存储验证码*/public static boolean putCaptcha(String captchaId, String code, int timeout, TimeUnit timeUnit) {long expireTime = System.currentTimeMillis() + timeUnit.toMillis(timeout);CACHE.put(captchaId, new CaptchaInfo(code, expireTime));// 清理过期数据cleanExpiredCache();return true;}/*** 验证验证码*/public static boolean verifyCaptcha(String captchaId, String inputCode, boolean removeAfterVerify) {CaptchaInfo info = CACHE.get(captchaId);if (info == null || info.isExpired()) {CACHE.remove(captchaId);return false;}boolean isValid = info.getCode().equalsIgnoreCase(inputCode.trim());if (removeAfterVerify) {CACHE.remove(captchaId);}return isValid;}/*** 清理过期缓存*/private static void cleanExpiredCache() {CACHE.entrySet().removeIf(entry -> entry.getValue().isExpired());}/*** 获取缓存大小(用于监控)*/public static int getCacheSize() {cleanExpiredCache();return CACHE.size();}
}

步骤2:创建服务接口

// src/main/java/cn/jbolt/admin/user/service/CaptchaService.java
package cn.jbolt.admin.user.service;import cn.jbolt.util.Result;public interface CaptchaService {/*** 生成验证码(默认参数)*/Result generateCaptcha();/*** 生成验证码(自定义参数)*/Result generateCaptcha(int width, int height, int codeCount, int lineCount);/*** 验证验证码*/Result verifyCaptcha(String captchaId, String inputCode);/*** 刷新验证码*/Result refreshCaptcha();
}

步骤3:实现服务类

// src/main/java/cn/jbolt/admin/user/service/impl/CaptchaServiceImpl.java
// [完整代码见上文服务层实现]

步骤4:创建控制器

// src/main/java/cn/jbolt/admin/user/controller/CaptchaController.java
// [完整代码见上文控制器层实现]

步骤5:创建前端验证码组件

<!-- src/components/verificationCode.vue -->
<!-- [完整代码见上文前端组件实现] -->

步骤6:在登录页面集成

<!-- src/views/login/index.vue -->
<!-- [完整代码见上文登录页面集成] -->

⚙️ 配置说明

1. 验证码参数配置

// 在 CaptchaServiceImpl.java 中修改默认配置
private static final int DEFAULT_WIDTH = 200;        // 图片宽度
private static final int DEFAULT_HEIGHT = 100;       // 图片高度  
private static final int DEFAULT_CODE_COUNT = 4;     // 验证码字符数
private static final int DEFAULT_LINE_COUNT = 20;    // 干扰线数量
private static final int DEFAULT_EXPIRE_MINUTES = 5; // 过期时间(分钟)

2. 系统配置开关

在数据库配置表中添加:

-- 验证码开关配置
INSERT INTO sys_config (config_key, config_value, description) 
VALUES ('jaOLT_LOGIN_USE_CAPTURE', 'true', '登录是否启用验证码');-- 版权信息配置
INSERT INTO sys_config (config_key, config_value, description) 
VALUES ('SYSTEM_COPYRIGHT_COMPANY', 'JBoltAI技术有限公司', '版权信息');

3. 前端配置

// 在 index.vue 中配置开发模式调试
const isDev = ref(process.env.NODE_ENV === 'development');// API接口配置
const API_BASE_URL = {development: 'http://localhost:8080',production: 'https://api.yourdomian.com'
}[process.env.NODE_ENV];

📚 API接口文档

1. 生成验证码

接口地址: GET /captcha/generate

请求参数:

响应示例:

{"code": 200,"msg": "验证码生成成功","data": {"captchaId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890","captchaImage": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABkCAYA..."}
}

2. 生成自定义验证码

接口地址: GET /captcha/generate/custom

请求参数:

参数名类型必填默认值说明
widthint200图片宽度
heightint100图片高度
codeCountint4验证码字符数
lineCountint20干扰线数量

请求示例:

GET /captcha/generate/custom?width=250&height=80&codeCount=5&lineCount=30

3. 验证验证码

接口地址: POST /captcha/verify

请求参数:

参数名类型必填说明
captchaIdString验证码ID
codeString用户输入的验证码

请求示例:

{"captchaId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890","code": "ABCD"
}

响应示例:

{"code": 200,"msg": "验证码验证成功","data": true
}

4. 刷新验证码

接口地址: GET /captcha/refresh

请求参数:

响应示例: 同生成验证码接口


❗ 常见问题解决

1. 验证码图片不显示

可能原因:

  • 后端接口未正确返回Base64数据
  • 前端图片src格式错误
  • 网络请求失败

解决方案:

// 1. 检查返回的Base64格式
console.log('验证码图片数据:', captcha.image);// 2. 确保Base64格式正确
if (captcha.image && !captcha.image.startsWith('data:image/')) {captcha.image = 'data:image/png;base64,' + captcha.image;
}// 3. 添加图片加载错误处理
const onImageError = () => {console.warn('验证码图片加载失败,正在重新获取...');refreshCaptcha();
};

2. 验证码验证失败

可能原因:

  • 验证码已过期
  • 大小写不匹配
  • 验证码ID与输入不对应

解决方案:

// 在验证时添加详细日志
@Override
public Result verifyCaptcha(String captchaId, String inputCode) {logger.info("验证码验证开始: captchaId={}, inputCode={}", captchaId, inputCode);// 添加更详细的验证逻辑CaptchaInfo info = CaptchaCache.getCaptcha(captchaId);if (info == null) {logger.warn("验证码不存在: captchaId={}", captchaId);return Result.error("验证码不存在或已过期");}if (info.isExpired()) {logger.warn("验证码已过期: captchaId={}", captchaId);CaptchaCache.removeCaptcha(captchaId);return Result.error("验证码已过期,请刷新");}// 忽略大小写比较boolean isValid = info.getCode().equalsIgnoreCase(inputCode.trim());logger.info("验证码验证结果: captchaId={}, isValid={}", captchaId, isValid);return isValid ? Result.success(true) : Result.error("验证码错误");
}

3. 内存缓存占用过多

解决方案:

// 添加定时清理任务
@Component
public class CaptchaCacheCleanTask {@Scheduled(fixedRate = 60000) // 每分钟执行一次public void cleanExpiredCache() {int beforeSize = CaptchaCache.getCacheSize();CaptchaCache.cleanExpiredCache();int afterSize = CaptchaCache.getCacheSize();if (beforeSize != afterSize) {logger.info("清理过期验证码缓存: {} -> {}", beforeSize, afterSize);}}
}

4. 前端组件状态同步问题

解决方案:

// 使用 watch 监听配置变化
watch(() => config.validateCodeEnable, (enabled) => {console.debug('验证码配置变化:', enabled);if (enabled && !captcha.image && !captcha.loading) {console.debug('配置启用后自动加载验证码');loadCaptcha();}
});// 添加组件状态调试
watch(() => captcha, (newCaptcha) => {console.debug('验证码状态变化:', {hasImage: !!newCaptcha.image,captchaId: newCaptcha.id,loading: newCaptcha.loading});
}, { deep: true });

🔧 扩展建议

1. 升级为Redis缓存

@Service
public class RedisCaptchaService {@Autowiredprivate RedisTemplate<String, String> redisTemplate;private static final String CAPTCHA_PREFIX = "captcha:";public boolean putCaptcha(String captchaId, String code, int timeout, TimeUnit timeUnit) {String key = CAPTCHA_PREFIX + captchaId;redisTemplate.opsForValue().set(key, code, timeout, timeUnit);return true;}public boolean verifyCaptcha(String captchaId, String inputCode, boolean removeAfterVerify) {String key = CAPTCHA_PREFIX + captchaId;String storedCode = redisTemplate.opsForValue().get(key);if (storedCode == null) {return false;}boolean isValid = storedCode.equalsIgnoreCase(inputCode.trim());if (removeAfterVerify) {redisTemplate.delete(key);}return isValid;}
}

2. 添加验证码类型扩展

public enum CaptchaType {LINE,      // 线性验证码CIRCLE,    // 圆圈验证码  SHEAR,     // 扭曲验证码GIF        // 动态验证码
}@Service
public class EnhancedCaptchaService {public Result generateCaptcha(CaptchaType type, int width, int height, int codeCount) {AbstractCaptcha captcha;switch (type) {case LINE:captcha = CaptchaUtil.createLineCaptcha(width, height, codeCount, 20);break;case CIRCLE:captcha = CaptchaUtil.createCircleCaptcha(width, height, codeCount, 20);break;case SHEAR:captcha = CaptchaUtil.createShearCaptcha(width, height, codeCount, 4);break;case GIF:captcha = CaptchaUtil.createGifCaptcha(width, height, codeCount);break;default:captcha = CaptchaUtil.createLineCaptcha(width, height, codeCount, 20);}// 其他逻辑保持不变return generateCaptchaResult(captcha);}
}

3. 添加验证码统计功能

@Component
public class CaptchaMetrics {private final AtomicLong generateCount = new AtomicLong(0);private final AtomicLong verifyCount = new AtomicLong(0);private final AtomicLong successCount = new AtomicLong(0);public void recordGenerate() {generateCount.incrementAndGet();}public void recordVerify(boolean success) {verifyCount.incrementAndGet();if (success) {successCount.incrementAndGet();}}public Map<String, Object> getMetrics() {Map<String, Object> metrics = new HashMap<>();metrics.put("generateCount", generateCount.get());metrics.put("verifyCount", verifyCount.get());metrics.put("successCount", successCount.get());metrics.put("successRate", verifyCount.get() > 0 ? (double) successCount.get() / verifyCount.get() : 0);return metrics;}
}

4. 前端增强功能

<script setup>
// 添加键盘事件支持
const handleKeydown = (event) => {if (event.key === 'Enter') {handleLogin();} else if (event.key === 'F5' || (event.ctrlKey && event.key === 'r')) {event.preventDefault();refreshCaptcha();}
};// 添加验证码倒计时
const countdown = ref(0);
const startCountdown = () => {countdown.value = 300; // 5分钟倒计时const timer = setInterval(() => {countdown.value--;if (countdown.value <= 0) {clearInterval(timer);refreshCaptcha(); // 自动刷新}}, 1000);
};// 添加无障碍支持
const speakCaptcha = () => {if ('speechSynthesis' in window) {const utterance = new SpeechSynthesisUtterance('请输入验证码');speechSynthesis.speak(utterance);}
};onMounted(() => {window.addEventListener('keydown', handleKeydown);
});onUnmounted(() => {window.removeEventListener('keydown', handleKeydown);
});
</script>

📝 总结

本技术文档详细介绍了基于Hutool工具类实现的完整验证码功能,包含:

  1. 完整的技术栈:Spring Boot + Hutool + Vue3 + Tailwind CSS
  2. 安全的设计:验证码ID与答案分离,过期自动清理
  3. 良好的用户体验:响应式设计,自动刷新,错误处理
  4. 可扩展性:支持Redis缓存,多种验证码类型,统计功能
  5. 详细的实现步骤:从零开始的完整实现指南

通过这个文档,即使是技术小白也能够:

  • 理解验证码功能的完整架构
  • 按步骤实现所有功能
  • 解决常见问题
  • 根据需要进行功能扩展

希望这个文档能够帮助您快速掌握验证码功能的开发!

http://www.xdnf.cn/news/10788.html

相关文章:

  • Nginx 响应头 Vary 的介绍与应用
  • YOLO学习笔记 | 一种用于海面目标检测的多尺度YOLO算法
  • 在前端使用JS生成音频文件并保存到本地
  • day18 leetcode-hot100-36(二叉树1)
  • tauri项目绕开plugin-shell直接调用可执行文件并携带任意参数
  • 【深度学习】大模型MCP工作流原理介绍、编写MCP
  • 谷歌地图2022高清卫星地图手机版v10.38.2 安卓版 - 前端工具导航
  • 小白的进阶之路系列之十一----人工智能从初步到精通pytorch综合运用的讲解第四部分
  • Franka科研新力量——基于异构预训练Transformer的扩展研究
  • 智能氮气柜的发展历程和前景展望
  • 从基础原理到Nginx实战应用
  • 架构设计的目标:高内聚、低耦合的本质
  • Pointer Network
  • FreeRTOS,其发展历程详细时间线、由来、历史背景
  • STM32学习之WWDG(原理+实操)
  • Go基础|map入门
  • 2025 Java面试大全技术文章(面试题1)
  • ABP-Book Store Application中文讲解 - Part 6: Authors: Domain Layer
  • (三)动手学线性神经网络:从数学原理到代码实现
  • C++初识—面向对象
  • JavaScript async/await指南
  • 亚远景科技助力东风日产通过ASPICE CL2评估
  • 【数据中心设计】
  • Vehicle HAL(3)--VehicleHalManager 分析
  • 【2025年B卷】OD-100分-斗地主之顺子
  • OD 算法题 B卷【跳格子2】
  • MTK的Download agent是什么下载程序?
  • 网络编程(计算机网络基础)
  • MyBatis 的动态 SQL
  • vSOME/IP与ETAS DSOME/IP通信的问题解决方案