NestJS 集成图片验证码服务

所需依赖

首先,我们需要安装验证码包svg-captcha来生成图片验证码

bash 复制代码
pnpm i svg-captcha

代码配置

typescript 复制代码
import { randomUUID } from 'crypto'
import svgCaptcha from 'svg-captcha'
import { Injectable } from '@nestjs/common'
import { RedisService } from './redis.service'
import { BusinessException, RedisConstant } from '@/common'

@Injectable()
export class CaptchaService {
 /** 验证码过期时间(秒) */
 private readonly CAPTCHA_EXPIRES_IN = 60

 constructor(private readonly redisService: RedisService) {}

 /**
  * 生成图形验证码
  * @description 生成数学表达式验证码并缓存答案,返回 base64 格式图片
  * @returns 验证码唯一标识和 base64 图片数据
  */
 public async create(): Promise<{ uuid: string; captcha: string }> {
   // 1. 生成唯一标识,用于关联验证码答案与图片
   const uuid = randomUUID()
   // 2. 创建数学表达式验证码
   const { data, text } = svgCaptcha.createMathExpr({ background: '#C0C8BE', noise: 4 })
   // 3. 转换为 base64 格式,前端可直接通过 img 标签渲染
   const captcha = this.convertToBase64(data)
   // 4. 缓存验证码答案,有效期 60 秒
   const key = this.getCacheKey(uuid)
   await this.redisService.set(key, text, 'EX', this.CAPTCHA_EXPIRES_IN)
   // 5. 返回验证码数据
   return { uuid, captcha }
 }

 /**
  * 校验图形验证码
  * @description 验证用户输入的验证码是否正确,校验通过后立即删除缓存防止重复使用
  * @param uuid - 验证码唯一标识
  * @param captcha - 用户输入的验证码
  * @throws {BusinessException} 验证码错误时抛出异常
  */
 public async validate(uuid: string, captcha: string): Promise<boolean> {
   // 1. 参数校验
   if (!uuid || !captcha) throw new BusinessException('验证码校验参数不完整')
   // 2. 获取缓存的验证码答案
   const key = this.getCacheKey(uuid)
   const cachedValue = await this.redisService.get(key)
   // 3. 验证码已过期或不存在
   if (!cachedValue) throw new BusinessException('验证码已过期,请刷新后重试')
   // 4. 验证码匹配校验(不区分大小写)
   const isValid = cachedValue.toLowerCase() === captcha.toLowerCase()
   if (!isValid) throw new BusinessException('验证码错误,请刷新后重试')
   // 5. 校验通过,立即删除缓存,防止重复使用
   await this.redisService.del(key)
   // 6. 返回校验成功标识
   return true
 }

 /**
  * 获取 Redis 缓存 Key
  * @param uuid - 验证码唯一标识
  */
 private getCacheKey(uuid: string): string {
   return `${RedisConstant.CAPTCHA_KEY}:${uuid}`
 }

 /**
  * 将 SVG 字符串转换为 base64 格式
  * @param svgData - SVG 原始数据
  * @returns base64 格式的图片数据
  */
 private convertToBase64(svgData: string): string {
   const base64 = Buffer.from(svgData).toString('base64')
   return `data:image/svg+xml;base64,${base64}`
 }
}
相关推荐
刚子编程4 天前
从零开始:在 Windows 服务器上部署 Node.js 项目(小白实战教程)
服务器·nestjs·pm2·windowsserver·node.js部署·caddy反向代理
CSharp精选营4 天前
从零开始:在 Windows 服务器上部署 Node.js 项目(小白实战教程)
nestjs·pm2·windowsserver·node.js部署·caddy反向代理
晓杰'5 天前
从0到1实现Balatro游戏后端(8):Skip Blind与Tag奖励机制设计与实现
后端·websocket·typescript·项目实战·nestjs·状态管理·游戏服务器
小蜜蜂dry9 天前
nestjs实战-权限二:角色模块
前端·后端·nestjs
小蜜蜂dry9 天前
nestjs实战-权限一: 菜单模块
前端·后端·nestjs
妖孽白YoonA12 天前
xlt-token v1.0.0 正式发布:NestJS / Express 一包接入的 Token 鉴权库
后端·node.js·nestjs
晓杰'13 天前
从0到1实现Balatro游戏后端(7):Boss Blind与特殊规则实现
后端·websocket·typescript·node.js·游戏开发·项目实战·nestjs
晓杰'13 天前
从0到1实现Balatro游戏后端(6):Blind关卡状态设计与回合推进实现
后端·websocket·typescript·游戏开发·项目实战·nestjs·状态管理
晓杰'20 天前
从0到1实现Balatro游戏后端(5):得分计算与单局结算流程实现
后端·typescript·node.js·游戏开发·项目实战·nestjs·webscoket
向上的车轮20 天前
NestJS 的优秀替代框架——系统化选型指南(2026视角)
nestjs