Node.js验证码:生成到验证的奇妙历程

Node.js验证码:从生成到验证的趣味探索之旅

📜 引言:为何需要验证码?

设想你经营着一家热门咖啡店,每日有大量顾客前来消费。突然有机器人不断下单,导致真实顾客难以买到饮品。验证码就如同咖啡店的‘人工甄别系统’,确保每个请求都来自真实的人类顾客。

在Web开发领域,验证码(CAPTCHA)主要发挥以下作用:

  • 抵御机器人暴力破解 (例如登录密码的暴力尝试)
  • 保护敏感接口 (像查询个人资料的接口)
  • 减轻服务器负担 (过滤掉无效的请求)

验证码是现代Web应用中常见的安全机制,能有效防范机器人的恶意攻击。接下来,我们将深入探究Node.js中验证码的生成与验证过程,这就像一场充满趣味的探索之旅!

1. 验证码的基础原理 🧠

验证码(CAPTCHA)的全称是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)。其核心思想是:

“创建一种人类易于识别但计算机难以识别的测试”

验证码工作流程示意图



客户端请求验证码
服务器生成SVG验证码
转换为PNG图片
返回Base64编码图片
用户提交表单+验证码
服务器校验验证码
验证通过?
执行业务逻辑
返回错误提示

2. 技术栈筹备 🛠️

在本次示例中,运用了以下关键技术:

技术 作用 特点
express Web框架 轻巧灵活,用于处理HTTP请求
svg-captcha 生成SVG验证码 纯JS实现,无需额外编译
sharp 图像处理 高性能的图片转换工具
express-session 会话管理 用于存储验证码文本

3. 验证码生成详解 🎨

让我们剖析代码里验证码生成的部分:

3.1 生成SVG验证码

const svgCaptcha = require('svg-captcha');
const sharp = require('sharp');
const session = require('express-session');

// 配置session中间件
router.use(session({
    secret: 'stu_xpx_200701',
    resave: false,
    saveUninitialized: true,
    cookie: { secure: false } // 生产环境建议设置为true(HTTPS)
}));

// 生成验证码路由
router.get('/captcha', async (req, res) => {
    // 创建包含4个字符的彩色验证码(排除易混淆字符)
    const captcha = svgCaptcha.create({
        size: 4,          // 4个字符
        ignoreChars: '0o1i', // 排除易混淆字符
        noise: 2,         // 干扰线数量
        color: true       // 彩色显示
    });

    // 存储验证码(带1分钟过期时间)
    req.session.captcha = {
        text: captcha.text.toLowerCase(), // 转为小写存储
        expiresAt: Date.now() + 60000     // 当前时间+1分钟
    };

💡 关键点:

参数 说明
size 4 验证码包含4个字符
ignoreChars ‘0o1i’ 排除数字0、字母o、数字1、字母i等易混淆字符
noise 2 添加2条干扰线增加识别难度
color true 使用彩色而非黑白

3.2 转换为PNG格式

为何要将SVG转为PNG?

45% 30% 25% 图片格式选择原因 兼容性 安全性 性能

借助sharp库进行转换:

    try {
        // 将SVG转为PNG(兼容更多浏览器)
        const pngBuffer = await sharp(Buffer.from(captcha.data))
            .png()
            .toBuffer();

        // 返回Base64编码的图片
        res.type('png').json({
            code: 0,
            data: `data:image/png;base64,${pngBuffer.toString('base64')}`,
            msg: "验证码图片获取成功"
        });
     } catch (err) {
        console.error('生成PNG失败:', err);
        res.status(500).json({ code: -1, msg: "生成验证码失败" });
    }

🔧 技术细节:

  1. Buffer.from() 将SVG字符串转为二进制数据
  2. sharp() 进行图片格式转换
  3. toString('base64') 生成前端可直接显示的DataURL

3.3 存储验证码信息

我们不仅存储验证码文本,还存储过期时间:

req.session.captcha = {
    text: captcha.text.toLowerCase(), // 验证码文本
    expiresAt: Date.now() + 1 * 60 * 1000 // 1分钟后过期
};

这种设计比单纯存储文本更安全,原因在于:

  1. 强制验证码有时效性
  2. 防止重放攻击
  3. 自动清理过期验证码

4. 验证码验证流程 🔍

当用户提交表单时,需要对验证码进行验证:

4.1 验证步骤分解

用户 服务器 提交表单(姓名,身份证,验证码) 检查必填字段 检查验证码是否存在 检查验证码是否过期 比较验证码文本 返回验证结果 用户
服务器

4.2 关键验证代码

router.post("/query", async (req, res) => {
    // 检查必填参数
    if (!req.body.studentname || !req.body.id_card || !req.body.code) {
        return res.status(400).json({ code: -1, msg: "缺少必要参数" });
    }

    // 验证码三重校验
    if (!req.session.captcha) {
        return res.status(400).json({ code: -1, msg: "验证码已过期" });
    }
    if (req.session.captcha.expiresAt < Date.now()) {
        return res.status(400).json({ code: -1, msg: "验证码已过期" });
    }
    if (req.session.captcha.text !== req.body.code.toLowerCase()) {
        return res.status(400).json({ code: -1, msg: "验证码错误" });
    }

    // 验证通过后立即销毁验证码(一次性使用)
    delete req.session.captcha;
    // 验证码通过后的代码逻辑...

🛡️ 安全策略:

  • 时间过期机制 :1分钟后自动失效
  • 大小写无关校验 :统一转为小写比较
  • 一次性使用 :验证后立即删除session存储

4.3 验证码的一次性使用

验证通过后立即删除验证码,避免重复使用:

// 验证通过后删除验证码(一次性使用)
delete req.session.captcha;

5. 成果查验

按照上述操作就能生成一个png地址,我们可以对其有效性进行检查,步骤如下:

生成的地址如:

返回地址:data:image/png;base64,${pngBuffer.toString('base64')}

例如:
data:image/png;base64,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
版权声明:程序员胖胖胖虎阿 发表于 2025年8月16日 上午10:33。
转载请注明:Node.js验证码:生成到验证的奇妙历程 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...