如何在Django的HTML表单中集成reCAPTCHA V3?问题排查
问题概述
在Django项目中完成reCAPTCHA公私密钥配置,表单类中添加ReCaptchaField后,前端模板渲染验证码字段失败,尝试JS集成但在Django模板中实现逻辑不清晰,需排查前后端问题。
代码中的显性错误
前端模板拼写失误
create.html中验证码字段渲染代码写成了{{ form.catpcha }},正确拼写应为{{ form.captcha }}——这是导致字段无法渲染的直接原因。
前端排查与修复
引入reCAPTCHA v3核心脚本
reCAPTCHA v3为隐形验证机制,必须引入官方脚本才能正常工作。在模板的<head>或表单所在的<body>末尾添加:<script src="https://www.google.com/recaptcha/api.js?render={{ RECAPTCHA_PUBLIC_KEY }}"></script>注:需在视图中将
RECAPTCHA_PUBLIC_KEY传入模板上下文,或确保模板可直接访问settings变量(可通过django.template.context_processors.settings启用)。添加表单提交的JS逻辑
v3需要在表单提交前获取验证令牌,并将其作为隐藏字段提交。在模板中补充脚本:<script> document.getElementById('create-form').addEventListener('submit', function(e) { e.preventDefault(); grecaptcha.execute('{{ RECAPTCHA_PUBLIC_KEY }}', {action: 'register'}).then(function(token) { // 创建隐藏字段存储令牌 let tokenInput = document.createElement('input'); tokenInput.type = 'hidden'; tokenInput.name = 'g-recaptcha-response'; tokenInput.value = token; document.getElementById('create-form').appendChild(tokenInput); // 提交表单 this.submit(); }.bind(this)); }); </script>验证字段渲染
修正拼写错误后,{{ form.captcha }}会生成reCAPTCHA所需的隐藏字段或标记,v3无需显示控件,确保字段能正常生成即可。
后端排查与修复
依赖与配置检查
确保已安装django-recaptcha包,并将captcha添加到INSTALLED_APPS:INSTALLED_APPS = [ # ... 其他应用 'captcha', ]表单类优化
使用django-recaptcha的ReCaptchaField时,无需手动指定公私密钥(settings中已配置即可),且v3的widget会自动处理验证逻辑,无需额外配置密钥参数:class createNewAccountForm(forms.Form): name = forms.CharField(required=True, validators=[validate_alphanumericplus]) email = forms.CharField(required=True) password = SetPasswordField(label='Password', required=True) captcha = ReCaptchaField( widget=ReCaptchaV3( attrs={ 'required_score': 0.85, 'action': 'register', # 需与前端JS中的action参数一致 } ) )视图类修复
- 无需在
form_valid中手动调用verify,ReCaptchaField会在表单验证阶段自动完成校验 return self.form_invalid写法错误,必须传递form参数,正确写法为return self.form_invalid(form)
优化后的视图代码:
class createNewAccountView(FormView): form_class = createNewAccountForm template_name = 'account/create.html' def form_valid(self, form): # 表单已通过captcha验证,直接处理用户创建逻辑 print("form is valid, creating user") print(form.cleaned_data) name = form.cleaned_data['name'] email = form.cleaned_data['email'] password = form.cleaned_data['password'] next = form.data.get('next', None) # ... 补充用户创建及后续逻辑 return super().form_valid(form) # 调用父类方法完成默认跳转- 无需在
reCAPTCHA v3 核心集成要点(翻译自官方文档)
reCAPTCHA v3是无干扰式验证方案,无需用户手动交互,通过分析用户页面行为生成0-1之间的分数:
- 分数越接近1,代表用户为真人的概率越高;分数越接近0,代表越可能是机器人
- 集成需引入官方脚本,通过
grecaptcha.execute获取验证令牌- 后端使用私钥向谷歌验证API发起请求,校验令牌有效性及分数
- 可根据业务需求设置最低分数阈值,拦截高风险请求
内容的提问来源于stack exchange,提问作者Bartosz Tiedemann

