添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在nestjs中使用图形验证码

发布时间: 2023-10-02

在文章开始之前我们先了解什么是图形验证码,以及图形验证码的用途。

图形验证码

图形验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”的缩写,中文常称为“全自动区分计算机和人类的图灵测试”。它是一种区分用户是计算机还是人的公共全自动程序。通常,图形验证码会要求用户输入在图片上显示的一串字符。

图形验证码的组成

图形验证码通常由以下几个部分组成:

  • 字符集 :验证码上显示的字符,通常是字母、数字或二者的组合。
  • 噪点和干扰线 :为了防止计算机程序识别字符,验证码通常会加入噪点和干扰线。
  • 背景 :验证码的背景通常会有颜色、图案等,以增加识别的难度。
  • 图形验证码的用途

    图形验证码主要用于防止恶意软件自动进行某些操作,其用途包括但不限于:

  • 防止暴力攻击 :通过限制只有通过验证码验证的请求才能继续,可以防止暴力破解密码等攻击。
  • 防止垃圾邮件 :在用户注册邮箱或发送邮件时使用验证码,可以有效减少垃圾邮件。
  • 防止恶意注册 :在用户注册时使用验证码,可以防止机器人大量自动注册账号。
  • 防止网络爬虫 :一些网站使用验证码来防止网络爬虫抓取网站数据。
  • 增加安全性 :在进行敏感操作时,如修改密码、转账等,使用验证码可以增加账户的安全性。
  • 图形验证码的优点和缺点

    优点

    1. 简单易用 :用户只需输入图片上的字符即可。
    2. 安全性较高 :由于加入了噪点、干扰线等,机器识别的难度较大。
    3. 用户体验 :对于视觉障碍的用户,图形验证码可能会带来使用上的困难。
    4. 可破解性 :随着图像识别技术的发展,一些简单的图形验证码可能会被机器识别。
    5. 图形验证码是网络安全中常用的一种工具,它通过向用户展示一张图片,要求用户输入图片上的字符,从而验证请求是否来自真实的人类用户。图形验证码广泛应用于各种场景中,如用户注册、登录、发帖等,以防止机器人和恶意软件的自动操作。

      实战

      在NestJS中,使用图形验证码登录通常涉及到几个步骤:生成验证码、发送验证码给前端、验证用户输入的验证码。

      生成验证码

      确实, svg-captcha 是一个非常流行的生成图形验证码的库,它可以生成SVG格式的验证码,这种格式的验证码可以很好地适应不同大小的屏幕。下面是在NestJS中使用 svg-captcha 库的一个简单例子。

      1. 安装 svg-captcha

      npm install svg-captcha

      2. 创建Captcha Service

      // captcha.service.ts
      import { Injectable } from '@nestjs/common';
      import * as svgCaptcha from 'svg-captcha';
      @Injectable()
      export class CaptchaService {
        private captcha: svgCaptcha.Captcha;
        generateCaptcha() {
          this.captcha = svgCaptcha.create();
          return this.captcha.data;
        validateCaptcha(userInput: string) {
          return this.captcha.text === userInput;
      

      3. 创建Auth Controller

      // auth.controller.ts
      import { Controller, Get, Post, Body, Res } from '@nestjs/common';
      import { Response } from 'express';
      import { CaptchaService } from './captcha.service';
      @Controller('auth')
      export class AuthController {
        constructor(private readonly captchaService: CaptchaService) {}
        @Get('captcha')
        getCaptcha(@Res() res: Response) {
          const captchaSvg = this.captchaService.generateCaptcha();
          res.type('image/svg+xml');
          res.send(captchaSvg);
        @Post('login')
        login(@Body('username') username: string, @Body('password') password: string, @Body('captcha') captcha: string) {
          if (!this.captchaService.validateCaptcha(captcha)) {
            return { success: false, message: '验证码错误' };
          // 在这里添加其他登录逻辑,例如验证用户名和密码
          return { success: true, message: '登录成功' };
      

      4. 在Module中导入Service和Controller

      // app.module.ts
      import { Module } from '@nestjs/common';
      import { AuthController } from './auth.controller';
      import { CaptchaService } from './captcha.service';
      @Module({
        imports: [],
        controllers: [AuthController],
        providers: [CaptchaService],
      export class AppModule {}

      5. 使用

      • 请求GET /auth/captcha来获取SVG格式的验证码。
      • 使用获取到的验证码,用户可以输入验证码,并与用户名和密码一起,通过POST /auth/login来登录。
      • 验证用户输入的验证码

        上面的代码示例中,验证码是在服务器的内存中存储的,这在某些情况下可能会导致问题。例如,如果你的应用运行在多个实例上,用户可能从一个实例获取验证码,但尝试在另一个实例上验证,这将导致验证失败。此外,如果服务器重新启动,存储在内存中的验证码将丢失。

        所以本示例将captcha.text存储在session中是一个很好的选择,这样可以确保每个用户的验证码是唯一的,并且可以在用户的多个请求之间保持状态。

        1. 安装express-session

        npm install express-session

        2. 修改Captcha Service

        // captcha.service.ts
        import { Injectable } from '@nestjs/common';
        import * as svgCaptcha from 'svg-captcha';
        @Injectable()
        export class CaptchaService {
          generateCaptcha() {
            return svgCaptcha.create();
        

        3. 修改Auth Controller

        // auth.controller.ts
        import { Controller, Get, Post, Body, Res, Session } from '@nestjs/common';
        import { Response } from 'express';
        import { CaptchaService } from './captcha.service';
        @Controller('auth')
        export class AuthController {
          constructor(private readonly captchaService: CaptchaService) {}
          @Get('captcha')
          getCaptcha(@Res() res: Response, @Session() session) {
            const captcha = this.captchaService.generateCaptcha();
            session.captchaText = captcha.text; // Store captcha text in session
            res.type('image/svg+xml');
            res.send(captcha.data);
          @Post('login')
          login(@Body('username') username: string, @Body('password') password: string, @Body('captcha') captcha: string, @Session() session) {
            if (captcha !== session.captchaText) {
              return { success: false, message: '验证码错误' };
            // 在这里添加其他登录逻辑,例如验证用户名和密码
            return { success: true, message: '登录成功' };
        

        4. 在Module中配置Session

        // app.module.ts
        import { Module } from '@nestjs/common';
        import { SessionModule } from '@nestjs/session';
        import { AuthController } from './auth.controller';
        import { CaptchaService } from './captcha.service';
        @Module({
          imports: [
            SessionModule.forRoot({
              session: { secret: 'my-secret', resave: false, saveUninitialized: false },
          controllers: [AuthController],
          providers: [CaptchaService],
        export class AppModule {}

        在这个改进的例子中,captcha.text被存储在session中,这样在用户登录时,可以通过比较session中存储的captcha.text和用户输入的验证码来进行验证。同时,由于每个用户都有一个唯一的session,所以每个用户都会有一个与之相关联的唯一验证码。

        cookie 跨域问题

        要解决跨域问题并设置session,你需要配置CORS(Cross-Origin Resource Sharing)以及session。以下是在NestJS中配置CORS和session的一种方法,并允许来自不同域的请求设置Cookie。

        1. 安装必要的包

        npm install express-session cors

        2. 配置CORS和Session

        在你的NestJS应用中,你需要配置CORS和session。以下是一个例子,展示了如何在main.ts文件中进行配置:

        import { NestFactory } from '@nestjs/core';
        import { AppModule } from './app.module';
        import * as session from 'express-session';
        import * as cors from 'cors';
        async function bootstrap() {
          const app = await NestFactory.create(AppModule);
          // 配置CORS
          app.enableCors({
            origin: 'http://your-frontend-domain.com', // 替换为你的前端域名
            credentials: true, // 允许发送Cookie
          // 配置Session
          app.use(
            session({
              secret: 'my-secret',
              resave: false,
              saveUninitialized: false,
              cookie: {
                httpOnly: true,
                secure: false, // 如果你的应用使用HTTPS,设置为true
                maxAge: 1000 * 60 * 60 * 24, // 1 day
                sameSite: 'None', // 如果你的应用使用HTTPS,并且需要支持跨域Cookie,设置为'None'
          await app.listen(3000);
        bootstrap();

        3. 配置前端请求

        在你的前端代码中,当你发送请求到后端时,你需要确保请求是带有credentials的。如果你使用的是fetch API,你可以这样做:

        fetch('http://your-backend-domain.com/api/some-endpoint', {
          method: 'GET',
          credentials: 'include', // 确保请求带有credentials
        

        如果你使用的是axios,你可以这样做:

        import axios from 'axios';
        axios.get('http://your-backend-domain.com/api/some-endpoint', {
          withCredentials: true, // 确保请求带有credentials