You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js中实现符合GDPR的简易数学验证码及后端验证?

在Next.js中实现自定义验证码(数学题+蜜罐)的后端验证方案

一、前端实现:生成数学题与蜜罐字段

  1. 生成随机数学题
    在React组件中生成两个随机数,计算正确答案,同时将这两个数通过隐藏字段传递给后端用于验证:

    import { useState } from 'react';
    
    export default function ContactForm() {
      const [a, setA] = useState(Math.floor(Math.random() * 10));
      const [b, setB] = useState(Math.floor(Math.random() * 10));
      const [userAnswer, setUserAnswer] = useState('');
      const [honeypot, setHoneypot] = useState(''); // 蜜罐字段
    
      const handleSubmit = async (e) => {
        e.preventDefault();
        const formData = new FormData();
        formData.append('a', a.toString());
        formData.append('b', b.toString());
        formData.append('userAnswer', userAnswer);
        formData.append('honeypot', honeypot);
        // 追加其他表单字段
    
        const res = await fetch('/api/submit-form', {
          method: 'POST',
          body: formData,
        });
    
        if (res.ok) {
          alert('提交成功');
          // 重置表单逻辑
        } else {
          const data = await res.json();
          alert(data.message);
        }
      };
    
      return (
        <form onSubmit={handleSubmit}>
          {/* 其他表单输入框(如姓名、邮箱、内容) */}
          <div className="captcha-group">
            <label>验证码:{a} + {b} = ?</label>
            <input
              type="number"
              value={userAnswer}
              onChange={(e) => setUserAnswer(e.target.value)}
              required
            />
          </div>
          {/* 蜜罐字段:用绝对定位移出可视区域,避免人类误填 */}
          <div style={{ position: 'absolute', left: '-9999px', top: 'auto' }}>
            <label>请忽略此字段</label>
            <input
              type="text"
              name="honeypot"
              value={honeypot}
              onChange={(e) => setHoneypot(e.target.value)}
            />
          </div>
          <button type="submit">提交</button>
        </form>
      );
    }
    
  2. 蜜罐字段核心设置

    • 不要用display: none隐藏字段(部分机器人会自动忽略这类元素),改用绝对定位将其移出可视区域
    • 字段名可设为website、full_name这类机器人常见的表单字段名,降低识别概率

二、后端验证逻辑(Next.js两种路由方案)

方案1:Pages Router(pages/api/目录)

创建pages/api/submit-form.js,处理表单提交并完成验证:

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ message: '仅支持POST请求' });
  }

  const { a, b, userAnswer, honeypot } = req.body;

  // 1. 蜜罐验证:非空则判定为机器人
  if (honeypot.trim() !== '') {
    return res.status(403).json({ message: '机器人验证失败' });
  }

  // 2. 数学题答案验证
  const correctAnswer = parseInt(a) + parseInt(b);
  if (isNaN(parseInt(userAnswer)) || parseInt(userAnswer) !== correctAnswer) {
    return res.status(403).json({ message: '验证码错误' });
  }

  // 3. 验证通过,执行业务逻辑(如发送邮件、存储数据)
  // ... 你的自定义代码

  res.status(200).json({ message: '提交成功' });
}

方案2:App Router(app/api/目录)

创建app/api/submit-form/route.js,用App Router语法实现:

export async function POST(request) {
  const formData = await request.formData();
  const a = formData.get('a');
  const b = formData.get('b');
  const userAnswer = formData.get('userAnswer');
  const honeypot = formData.get('honeypot');

  // 1. 蜜罐验证
  if (honeypot && honeypot.trim() !== '') {
    return Response.json({ message: '机器人验证失败' }, { status: 403 });
  }

  // 2. 数学题答案验证
  const correctAnswer = parseInt(a) + parseInt(b);
  if (isNaN(parseInt(userAnswer)) || parseInt(userAnswer) !== correctAnswer) {
    return Response.json({ message: '验证码错误' }, { status: 403 });
  }

  // 3. 执行表单业务逻辑
  // ...

  return Response.json({ message: '提交成功' }, { status: 200 });
}

三、额外优化建议

  • 若担心机器人读取前端隐藏字段的a、b直接计算答案,可引入session(如Iron Session库),将正确答案存在服务器端session中,前端仅展示题目,后端从session取答案对比
  • 可随机切换运算类型(加/减/乘),提升验证码复杂度
  • 提交失败后重新生成新的验证码,避免用户重复提交同一题目

内容的提问来源于stack exchange,提问作者T B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 10:30:45