依赖安装
pnpm i svg-captcha uuid @nestjs/cache-manager
创建并配置 captcha 模块
nest g res system/captcha --no-spec
更改 captcha.controller.ts
- 我们可以选择生成字母数字类型的或者数学计算的验证码
- 我们需要一个校验用户是否输入正确验证码的接口
import { Controller, Get, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { CaptchaService } from './captcha.service'; @ApiTags('本地验证码') @Controller('captcha') export class CaptchaController { constructor(private readonly captchaService: CaptchaService) {} @Get('svg') createSvg() { return this.captchaService.create('svg'); } @Get('math') createMath() { return this.captchaService.create('math'); } @Get('verify') get(@Query('key') key: string, @Query('text') text: string) { return this.captchaService.verify(key, text); } }
根据 captcha.controller.ts
需要的功能我们来更改 captcha.service.ts
- 这里使用 uuid 可以最大概率保证 key 的唯一性
- 记得 Cache 模块一定要从
@nestjs/cache-manager
包中导入 - 这里需要用到缓存模块,下一部分会说明
import { Cache } from '@nestjs/cache-manager'; import { BadRequestException, Injectable } from '@nestjs/common'; import * as svgCaptcha from 'svg-captcha'; import { v4 as uuidv4 } from 'uuid'; import { CacheKeys } from '@/common/enum/cache-keys.enum'; @Injectable() export class CaptchaService { constructor(private readonly cache: Cache) {} // 生成验证码,可以选择是否是数学计算 async create(type: 'math' | 'svg' = 'svg') { let captcha: svgCaptcha.CaptchaObj; if (type === 'math') { captcha = svgCaptcha.createMathExpr({ mathMin: 0, mathMax: 9, noise: 1, color: true, }); } else { captcha = svgCaptcha.create({ ignoreChars: '0o1i', noise: 1, color: true, }); } // 生成一个uuid,调用接口时需要把这个 key 和内容一起传过来,用来校验此次输入是否正确 const key = uuidv4(); try { // 将生成好的验证码的正确文本缓存起来,后续用来根据 key 做比对 await this.cache.set(`${CacheKeys.CAPTCHA_CACHE_KEY}:${key}`, captcha.text, 60 * 1000); // 缓存 1 分钟 } catch (error) { console.log(error, 'error'); } return { key, svg: captcha.data, }; } // 校验输入的验证码是否正确 async verify(key: string, text: string) { const captchaText = await this.cache.get<string>(`${CacheKeys.CAPTCHA_CACHE_KEY}:${key}`); if (captchaText !== text) { throw new BadRequestException('验证码错误'); } return true; } }
cache-keys.enum.ts
的内容为
export const CacheKeys = { CAPTCHA_CACHE_KEY: 'captcha', } as const;
配置缓存模块
在 app.module 中我们需要配置一下缓存模块,让它使用 redis 而不是默认的内存缓存
import { createKeyv } from '@keyv/redis'; import { CacheModule } from '@nestjs/cache-manager'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@/config/config.module'; import { ConfigService } from '@/config/config.service'; import { AppController } from './app.controller'; import { CaptchaModule } from './system/captcha/captcha.module'; @Module({ imports: [ CacheModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], isGlobal: true, // 这里有 global 后,别的模块使用时直接 `private readonly cache: Cache` 注入即可 useFactory(config: ConfigService) { return { stores: [ createKeyv({ url: `redis://${config.cache.redis.host}:${config.cache.redis.port}`, password: config.cache.redis.password, }), ], ttl: config.cache.ttl, }; }, }), CaptchaModule, ], controllers: [AppController], providers: [AppService], }) export class AppModule {}
这里的 config 模块使用我们自己改变的一种写法(当然这个是在官方文档里有说明的),这种写法可以让我们在取用配置时有提示,我这里有另一篇文章来说明

清理代码
使用命令创建的 captcha 模块会有 entity 和 dto,这里我们用不到可以将其删除。