NestJs 结合 Redis 实现图形校验码登录,和手机验证码登录一样的流程

1,043 阅读5分钟

文章开始之前分享两个开源项目,会一直维护的,欢迎 star,如果你感兴趣或者想参与学习,可以加我微信 yunmz777,最近也在找工作 ing,欢迎内推......

浪费你两秒钟时间,我们正文开始!!!

在 NestJS 应用中结合 Redis 实现图形验证码登录功能可以提高应用的安全性,尤其是用于防止自动化攻击和机器人登录。下面是一个实现图形验证码登录的基本步骤,包括生成验证码、存储验证码到 Redis、验证用户输入的验证码,以及整合到登录流程中。

svg-captcha

svg-captcha 是一个在 Node.js 环境中广泛使用的库,用于生成 SVG 格式的图形验证码。它提供了一种简便的方法来创建包含随机文本的验证码图片,这些图片可以有效地防止自动化工具和脚本的干扰,从而保护网站免受自动化攻击和滥用。

它的主要特性有以下几个方面:

  1. SVG 格式:生成的验证码为 SVG 文件格式,这种格式文件大小较小,且易于缩放,不会失真。

  2. 高度可定制:svg-captcha 支持多种定制选项,包括文字大小、噪声线条、颜色、背景等。

  3. 包括噪声和扭曲:为了提高安全性,svg-captcha 可以在验证码中添加噪声线条和扭曲效果,这使得验证码难以被机器自动识别。

  4. 简单易用:API 简单,易于集成和使用,只需几行代码即可生成验证码。

要想使用 svg-captcha,我们首先需要安装该包:

pnpm add svg-captcha

以下是一个基本的使用示例,如下代码所示:

const svgCaptcha = require("svg-captcha");

// 创建一个验证码
const captcha = svgCaptcha.create({
  size: 6, // 验证码长度
  ignoreChars: "0o1i", // 排除 0o1i
  noise: 2, // 噪声线条数量
  color: true, // 验证码的字符有颜色,而不是黑白
  background: "#cc9966", // 背景颜色
});

console.log(captcha.data); // 输出 SVG 图像
console.log(captcha.text); // 输出验证码文本,用于后端验证

执行文件输出的结果如下图所示:

20240429083417

它的文本内容是 4DmLVh,接下来我们打开这个 svg 文件查看是不是跟这个文本一样的:

20240429083541

和我们这个生成的文本一毛一样,接下来我们将在 nestjs 中集成来实现一个简单的登录功能。

nestjs 中集成

在接下来的内容中我们将实现验证登录,并不对登录成功之后的内容进行处理,熟悉一下大概流程即可。

首先我们创建一个 login 模块,如下图所示:

20240429083936

首先我们应该先创建一个 controller 用于生成图形验证码的,如下所示:

20240429084652

这个时候我们就可以编写我们的服务了,如下代码所示:

import { Injectable } from "@nestjs/common";
import * as svgCaptcha from "svg-captcha";
import { v4 as uuid } from "uuid";

import { RedisService } from "@/common/redis/redis.service";

@Injectable()
export class LoginService {
  constructor(private readonly redisService: RedisService) {}
  async generateCaptcha() {
    // 创建一个验证码
    const captcha = svgCaptcha.create({
      size: 6, // 验证码长度
      ignoreChars: '0o1i', // 排除 0o1i
      noise: 2, // 噪声线条数量
      color: true, // 验证码的字符有颜色,而不是黑白
      background: '#cc9966' // 背景颜色
    });

    const uniqueId = uuid();

    const result = await this.redisService.set(uniqueId, captcha.text, 60);

    const svgData = Buffer.from(captcha.data).toString('base64');
    console.log(captcha.text);

    if (result === 'OK') {
      return {
        data: {
          key: uniqueId,
          data: svgData
        }
      };
    }
  }

在上面的代码中我们设置了一个会过期的图形验证码,只有在 60 秒内有效,最终返回了一个图形验证和唯一的 id 来确认是不是同一个用户来获取的图像验证码。

最终访问接口,如下所示:

20240429094556

接下来我们就可以实现一个简单的登录流程了,我们要编写一个 dto,用来检验用户的登录信息:

import { IsString, IsNotEmpty, MinLength } from "class-validator";

export class LoginDto {
  @IsNotEmpty({ message: "账号不能为空" })
  @IsString({ message: "账号必须为字符串" })
  username: string;

  @IsNotEmpty({ message: "密码不能为空" })
  @IsString({ message: "密码必须为字符串" })
  @MinLength(6, { message: "密码长度不能小于6个字符" })
  password: string;

  @IsNotEmpty({ message: "key不能为空" })
  @IsString({ message: "key必须为字符串" })
  key: string;

  @IsNotEmpty({ message: "验证码不能为空" })
  @IsString({ message: "验证码必须为字符串" })
  captcha: string;
}

之后我们来编写一个登录的路由,也就是我们的控制器:

20240429092720

之后就可以编写我们的服务了,如下代码所示:

20240429093235

这里我们的登录写得很简答, 主要是验证验证码是否存在且相同,如果相同的话则可以进入后续的流程。

之后我们就可以测试一下我们的代码了,首先我们来获取一下图像验证码:

20240429094634

我们再来看看这个生成的 svg 是什么,这个时候我们需要编写一下代码

20240429094733

客户端接收到这样的响应后,需要将 Base64 编码的字符串解码并正确处理。例如,如果是在 Web 页面上,可以这样使用:

<img src="data:image/svg+xml;base64,{{captcha}}" />

这个时候我们输入验证码,发现错误了,是因为我们已经超过了 60 秒了,所以返回错误了,需要我们重新获取

20240429094832

下图是我们重新获取到的验证码和唯一的 key:

20240429094932

之后我们登录的时候就可以根据图形验证码所看到的字符串传递进去,这样我们的验证码阶段就完成了:

20240429095022

总结

通过本文我们学习了使用 nestjs 结合 redis 实现了一个图形验证码的登录流程,在登录的时候首先验证码是否有效,有效的话就可以进入登录的流程,如果无效直接退出,手机验证码也是同理的。