如何在Next.js中实现符合GDPR的简易数学验证码及后端验证?
在Next.js中实现自定义验证码(数学题+蜜罐)的后端验证方案
一、前端实现:生成数学题与蜜罐字段
生成随机数学题
在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> ); }蜜罐字段核心设置
- 不要用
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
相关产品推荐
相关产品推荐

