在Web应用中,为了防止恶意的自动化攻击(如暴力破解密码),通常会添加验证码机制。本文将详细介绍如何在NestJS应用中实现图片验证码功能,并进行效验。我们将分步骤讲解如何生成验证码、展示在前端以及效验用户输入的验证码。
生成验证码
首先,我们需要安装验证码包svg-captcha
来生成图片验证码
yarn add svg-captcha
定义验证码controller
import {
Controller,
Get,
Inject,
Request,
Res,
Session
} from '@nestjs/common';
import { Response } from 'express';
import { CaptchaService } from '../services/captcha.service';
@Controller('captcha')
export class CaptchaController {
@Inject(CaptchaService)
private captchaService: CaptchaService;
@Get('svg')
getSvgCaptcha(@Res() response: Response, @Session() session) {
try {
const { data, text } = this.captchaService.createSvgCaptcha();
session.code = text; // 存储验证码文本在session中
response.set('Content-Type', 'image/svg+xml');
response.send(data);
} catch (error) {
console.error(error);
}
}
}
- 在上述代码中我们在
session
中设置code
的值为验证码的值text
,可以用于登录时效验验证码是否正确;如果说,你遇到如下错误,说明在你的项目中并未初始化Session
对象
Cannot set properties of undefined (setting 'code')
- 那么你需要安装
express-session
或类似的Session
中间件,并进行配置:
yarn add express-session
- 然后在
main.ts
中进行配置:
// 已省略部分代码
import * as session from 'express-session';
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false, // 表示如果session对象没有被修改,不会强制重新保存。
saveUninitialized: false, // 表示不会为未初始化的session对象保存到存储中。
// 同时设置rolling、maxAge,确保只要用户与服务器保持活动,他们的session就不会过期。
rolling: true, //在每次请求时强行设置 cookie,这将重置 cookie 过期时间(默认:false)
cookie: { maxAge: 5 * 60 * 1000 } // 5分钟过期时间
})
);
- 设置响应的
Content-Type
为image/svg+xml
,这里将svg
作为一个图片资源,可在img
标签的src
中进行使用,如:
<img src="/captcha" alt="Captcha">
定义service
使用svgCaptcha.create()
方法可以生成svg
的相关数据,data
是svg
的具体代码数据,而text
则是具体的验证码
import { Injectable } from '@nestjs/common';
import * as svgCaptcha from 'svg-captcha';
@Injectable()
export class CaptchaService {
createSvgCaptcha() {
const captcha = svgCaptcha.create({
size: 4, // 验证码长度
noise: 3, // 干扰线条的数量
width: 100, // 验证码宽度
height: 40, // 验证码高度
fontSize: 40, // 字体大小
color: true // 是否启用颜色
// background: '#f2f2f2' // 背景颜色
});
return {
data: captcha.data,
text: captcha.text.toLowerCase() // 转换为小写或进行其他处理
};
}
}
前端展示
要在前端显示后端返回的SVG代码,有几种方法可以实现。这些方法取决于你如何获取SVG数据,以及你希望在前端如何处理和显示它。
使用<img>
标签的src
属性
可以将SVG数据作为一个图片资源来处理,将后端返回的SVG代码放入<img>
标签的src
属性中。你需要确保后端的API端点返回SVG数据,并设置正确的Content-Type
为image/svg+xml
。部分代码如下:
<img
class="ml20"
style="width: 100px; height: 40px"
:src="codeUrl"
alt=""
@click="setCodeUrl"
/>
let codeUrl = ref<any>('')
const setCodeUrl = () => {
codeUrl.value = `api/captcha/svg?.=${Math.random()}`
}
onMounted(() => {
setCodeUrl()
})
在上述代码中,img
标签的src
会向api/captcha/svg?.=${Math.random()}
发起请求,后端返回相应的图片数据,并显示。
后端效验
后端效验即将前端传过来的code
与存在session
的code
相比较是否一致即可
@Post('login')
@ApiOperation({
summary: '登录' // 接口描述信息
})
async userLogin(@Body() loginUser: LoginUserDto, @Session() session) {
if (loginUser.code !== session.code) {
throw new BusinessException({
code: BUSINESS_ERROR_CODE.COMMON,
message: '验证码错误'
});
}
const userInfoVo = await this.userService.login(loginUser);
return userInfoVo;
}