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}`
 }
}
相关推荐
Allshadow5 天前
Nest.js - 连接数据库
javascript·nestjs
Allshadow8 天前
Nest.js - 目录结构
javascript·nestjs
南一Nanyi9 天前
依赖注入和控制反转
前端·设计模式·nestjs
晓得迷路了10 天前
栗子前端技术周刊第 138 期 - NestJS 12、Astro 7.1、npm 12...
前端·javascript·nestjs
xiaofeichaichai12 天前
NestJS 实战:JWT 登录认证、Token 刷新与安全退出
redis·nestjs·jwt·twitter
无双_Joney14 天前
[四期 - 4] NestJS专栏 - 征召一下大家的意见,非常想知道大家都想看看Nest的哪些方面
前端·后端·nestjs
糖墨夕15 天前
AI Agent技术栈选择:Vue3 + NestJS + TypeScript 为什么是绝佳组合
前端·nestjs
悟空瞎说1 个月前
企业级 NestJS + Prisma 分层架构:单例客户端、生命周期托管与可复用扩展实践
nestjs
悟空瞎说1 个月前
NestJS 接口设计避坑:摒弃万能用户更新接口,落地单一职责与最小权限原则
后端·nestjs
光影少年1 个月前
HashRouter 和 BrowserRouter 区别、底层原理、部署差异
前端·react.js·nestjs