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}`
 }
}
相关推荐
Darling噜啦啦4 小时前
NestJS 全栈 CRUD 实战:从 Module 拆分到 RESTful 七大装饰器,彻底搞懂后端接口工程
nestjs
嘟嘟07177 小时前
从入口到 CRUD:用 NestJS 的模块化与装饰器思想读懂一个后端应用
后端·typescript·nestjs
想要成为糕糕手7 小时前
🚀 NestJS 完全入门指南: Module/Controller/Service讲解 + 实战 CRUD
后端·nestjs
想要成为糕糕手8 小时前
🏭 设计模式之工厂模式:从蜜雪冰城到 NestJS,把「new」外包出去
后端·nestjs
小月土星8 小时前
用设计哲学的眼光重新审视 NestJS——一个 AI 应用后端的思考
nestjs
触底反弹8 小时前
🔥 NestJS 从零到实战:一个 Todos CRUD 搞懂企业级后端框架的核心设计
后端·typescript·nestjs
只一8 小时前
吃透 NestJS 核心|工厂模式 + 依赖注入 + Todo CRUD 实战,后端入门到上手
nestjs
Asize8 小时前
3 种设计模式 + DI:Nest.js 后端第一课
后端·设计模式·nestjs
moMo1 天前
深入 NestJS:从工厂模式到装饰器模式的架构之美
typescript·nestjs
HjhIron1 天前
NestJS 入门指南:从工厂模式到模块化 CRUD 实战
前端·nestjs