在文章开始之前我们先了解什么是图形验证码,以及图形验证码的用途。
图形验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”的缩写,中文常称为“全自动区分计算机和人类的图灵测试”。它是一种区分用户是计算机还是人的公共全自动程序。通常,图形验证码会要求用户输入在图片上显示的一串字符。
图形验证码通常由以下几个部分组成:
图形验证码主要用于防止恶意软件自动进行某些操作,其用途包括但不限于:
图形验证码是网络安全中常用的一种工具,它通过向用户展示一张图片,要求用户输入图片上的字符,从而验证请求是否来自真实的人类用户。图形验证码广泛应用于各种场景中,如用户注册、登录、发帖等,以防止机器人和恶意软件的自动操作。
在NestJS中,使用图形验证码登录通常涉及到几个步骤:生成验证码、发送验证码给前端、验证用户输入的验证码。
确实,svg-captcha
是一个非常流行的生成图形验证码的库,它可以生成SVG格式的验证码,这种格式的验证码可以很好地适应不同大小的屏幕。下面是在NestJS中使用svg-captcha
库的一个简单例子。
svg-captcha
npm install svg-captcha
// 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;
}
}
// 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: '登录成功' };
}
}
// 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 {}
GET /auth/captcha
来获取SVG格式的验证码。POST /auth/login
来登录。上面的代码示例中,验证码是在服务器的内存中存储的,这在某些情况下可能会导致问题。例如,如果你的应用运行在多个实例上,用户可能从一个实例获取验证码,但尝试在另一个实例上验证,这将导致验证失败。此外,如果服务器重新启动,存储在内存中的验证码将丢失。
所以本示例将captcha.text
存储在session中是一个很好的选择,这样可以确保每个用户的验证码是唯一的,并且可以在用户的多个请求之间保持状态。
express-session
npm install express-session
// captcha.service.ts
import { Injectable } from '@nestjs/common';
import * as svgCaptcha from 'svg-captcha';
@Injectable()
export class CaptchaService {
generateCaptcha() {
return svgCaptcha.create();
}
}
// 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: '登录成功' };
}
}
// 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,所以每个用户都会有一个与之相关联的唯一验证码。
要解决跨域问题并设置session,你需要配置CORS(Cross-Origin Resource Sharing)以及session。以下是在NestJS中配置CORS和session的一种方法,并允许来自不同域的请求设置Cookie。
npm install express-session cors
在你的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();
在你的前端代码中,当你发送请求到后端时,你需要确保请求是带有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
});
origin
选项设置为你的前端域名。cookie.secure
选项设置为true
,并将cookie.sameSite
选项设置为None
。credentials
。
.