依赖安装
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,这里我们用不到可以将其删除。