如何在Django中验证Google reCAPTCHA v2

8
我一直在尝试在我正在制作的网站上使用Google reCAPTCHA。验证码加载在网页上,但是我无法使用几种方法进行验证。我尝试了如何使用Python插件reCaptcha客户端进行验证?中提供的recaptcha验证方法,但我认为它已经过时了,不再起作用,并且它引用的是挑战(challenge),而我正在尝试使用的是Google的新“复选框”reCAPTCHA v2,或者我需要在安装recaptcha-client或django-recaptcha后进行设置更改。
请帮帮我!
6个回答

27

这里是一个简单的示例,用 requests 库(http://docs.python-requests.org/en/latest/)在 Django 视图中验证 Google reCAPTCHA v2:

import requests
from django.conf import settings

def get_client_ip(request):
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0]
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip

def grecaptcha_verify(request):
    if request.method == 'POST':
        response = {}
        data = request.POST
        captcha_rs = data.get('g-recaptcha-response')
        url = "https://www.google.com/recaptcha/api/siteverify"
        params = {
            'secret': settings.RECAPTCHA_SECRET_KEY,
            'response': captcha_rs,
            'remoteip': get_client_ip(request)
        }
        verify_rs = requests.get(url, params=params, verify=True)
        verify_rs = verify_rs.json()
        response["status"] = verify_rs.get("success", False)
        response['message'] = verify_rs.get('error-codes', None) or "Unspecified error."
        return HttpResponse(response)

1
你何时调用 grecaptcha_verify 方法? - AkaSh
1
@AkaSh 在调用 form.is_valid() 方法之前,我曾经调用过那个方法。 - Toan Nguyen

10

有一个第三方 Django 应用程序可以实现新的 reCAPTCHA v2,它在这里:

https://github.com/ImaginaryLandscape/django-nocaptcha-recaptcha

安装后,请将以下行添加到以下文件中:

# settings.py
NORECAPTCHA_SITE_KEY = <the Google provided site_key>
NORECAPTCHA_SECRET_KEY = <the Google provided secret_key>

INSTALLED_APPS = (
    ....
    'nocaptcha_recaptcha'
)


#forms.py
from nocaptcha_recaptcha.fields import NoReCaptchaField

class YourForm(forms.Form):
    .....
    captcha = NoReCaptchaField()


# In your template, add the following script tag:
<script src="https://www.google.com/recaptcha/api.js" async defer></script>

我尝试过了,但是这个字段在我的HTML模板中没有渲染出来。我正在使用自定义表单将数据提供给我的Django表单,并且我已经使用了一个单独的表单来使用Django {{ from.as_p }},但它并没有渲染出来。虽然该表单已经停止注册,所以我知道它是有效的,只是渲染还没有完成。 - Keshav Agarwal
1
在这种情况下,我建议您提出一个新的、与您的问题相关的具体问题。您当前的问题是如何在Django中设置reCAPTCHA v2(并添加了您尝试过为reCAPTCHA v1设计的reCAPTCHA应用程序),而这个答案回答了这个问题。您应该发布一个单独的问题列表,列出您的具体问题、您的代码以及您所做的故障排除。如果您愿意,您也可以将此答案标记为现有问题的正确答案,以使其他人受益。 - Christian Abbott

9
谷歌已经改变了API,现在我们需要使用POST请求。这是一个可重复使用的解决方案,如果您需要在多个Django视图中进行验证,请按如下步骤操作:

utils.py

# django imports
from django.conf import settings
from django.views.generic.base import View
from django.http import HttpResponseForbidden

# 3rd-party imports
import requests
from ipware import get_client_ip


def is_recaptcha_valid(request):
    """
    Verify if the response for the Google recaptcha is valid.
    """
    return requests.post(
        settings.GOOGLE_VERIFY_RECAPTCHA_URL,
        data={
            'secret': settings.RECAPTCHA_SECRET_KEY,
            'response': request.POST.get('g-recaptcha-response'),
            'remoteip': get_client_ip(request)
        },
        verify=True
    ).json().get("success", False)



def human_required(view_func):
    """
    This decorator is aimed to verify Google recaptcha in the backend side.
    """
    def wrapped(request, *args, **kwargs):
        if is_recaptcha_valid(request):
            return view_func(request, *args, **kwargs)
        else:
            return HttpResponseForbidden()
    return wrapped

then:

views.py

 from utils import human_required

 class MyView(View):

     @human_required
     def post(request, *args, **args):
        pass

注意,我们在这个解决方案中使用了 django-ipware 来获取IP地址,但这取决于您。此外,请不要忘记将 GOOGLE_VERIFY_RECAPTCHA_URLRECAPTCHA_SECRET_KEY 添加到django设置文件中!

我认为在 is_recaptcha_valid 中设置短路是个好主意,如果 POST 中不存在 'g-recaptcha-response',则立即返回 False - Tim Tisdall
是的,我同意!Tim - trinchet

2

views.py

def login(request):
    if request.method == 'POST':
    username = request.POST['username']
    password = request.POST['password']
    user = auth.authenticate(request, username=username, password=password)

     if user is not None:
        if user.is_active:
            auth.login(request, user)
            ''' Begin reCAPTCHA validation '''
            recaptcha_response = request.POST.get('g-recaptcha-response')
            url = 'https://www.google.com/recaptcha/api/siteverify'
            values = {
            'secret' : settings.GOOGLE_RECAPTCHA_SECRET_KEY,
            'response' :  recaptcha_response
            }
            data = urllib.parse.urlencode(values).encode("utf-8")
            req = urllib2.Request(url, data)
            response = urllib2.urlopen(req)
            result = json.load(response)
            ''' End reCAPTCHA validation '''

            if result['success']:
              return redirect('index')
            else:
              messages.error(request, 'Invalid reCAPTCHA. Please try again.')
              return redirect('login')
    else:
        messages.info(request, 'Wrong Credentials!!! enter right username or password')
        return redirect('login')
else:
    return render(request, 'login.html')

login.html

<form action="{% url 'login' %}" method="post">
            {% csrf_token %}
            <div class="body bg-gray">
                <div class="form-group">
                    <input type="text" name="username" class="form-control" placeholder="Username"/>
                </div>
                <div class="form-group">
                    <input type="password" name="password" class="form-control" placeholder="Password"/>
                </div>          
                <div class="form-group">
                    <input type="checkbox" name="remember_me"/> Remember me
                </div>
            </div>
            <div class="footer">                                                               
                 <button type="submit" class="btn bg-olive btn-block">Sign me in</button>

                <p><a href="#">I forgot my password</a></p>

                <a href="{% url 'register' %}" class="text-center">Register a new membership</a>
            </div>
            <br><br>
            <script src='https://www.google.com/recaptcha/api.js'></script>
            <div class="g-recaptcha" data-sitekey="(enter your key here that is private or authenticated on google recapthcha)"></div>
        </form>

settings.py

INSTALLED_APPS = [
   ....
   ....
   'captcha'
               ]

GOOGLE_RECAPTCHA_SECRET_KEY ='6LdXBLAUAMlGYqqyDESeHKI7-'
RECAPTCHA_PUBLIC_KEY = '6LdXBLAUAAAAAP3oI1VPJgA-VHXoj'
RECAPTCHA_PRIVATE_KEY = '6LdXBLAUAAAAAGYqqyDESeHKI7-'

''' you have to register your domain to get the access of these keys. or you can 
register your localhost also to test this after uploading on the server you can 
register with real domain and change the keys.

don't forget to like if you find it helpful.'''

 'https://www.google.com/recaptcha/intro/v3.html' -> 'admin console' where you can 
 register your domain or localhost and get your key. 

1
欢迎来到 Stack Overflow!感谢您提供如此出色的答案。如果您能够包含一些有关实现所需结果的附加评论,那就太好了。 - technogeek1995

1

在@trinchet给出的答案基础上,这里是对FormView Django类的简单修改,以自动处理Google的reCAPTCHA v2。

class RecaptchaFormView(FormView):
    """ This class handles Google's reCAPTCHA v2. """
    recaptcha_ok = None

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['grecaptcha_site_key'] = settings.RECAPTCHA_SITE_KEY
        return context

    def get_form(self):
        form = super().get_form()
        if self.recaptcha_ok == False:
            form.add_error(None, "Invalid reCAPTCHA, please try again.")
        return form

    def post(self, request, *args, **kwargs):
        self.recaptcha_ok = is_recaptcha_valid(request)
        return super().post(self, request, *args, **kwargs)

不要忘记包含@trinchet提供的is_recaptcha_valid函数(请参见他的答案),在settings.py中添加reCAPTCHA密钥,并在模板中添加reCAPTCHA代码(使用{{ grecaptcha_site_key }}作为站点密钥)。

0

这是我处理所提出问题的方式:

views.py

from django.contrib.auth.views import LoginView, LogoutView
from django.conf import settings

from authentication.forms import MyAuthenticationForm


class MyLoginView(LoginView):
   template_name = 'authentication/login.html'
   form_class = MyAuthenticationForm

   def get_client_ip(self):
       x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR')
       if x_forwarded_for:
           ip = x_forwarded_for.split(',')[0]
       else:
           ip = self.request.META.get('REMOTE_ADDR')
       return ip

   def get_form_kwargs(self):
       kwargs = super(MyLoginView, self).get_form_kwargs()
       if self.request.method in 'POST':
           kwargs['g-recaptcha-response'] = self.request.POST.get('g-recaptcha-response')
           kwargs['remote_ip'] = self.get_client_ip()
       return kwargs

   def get_context_data(self, **kwargs):
       context = super(MyLoginView, self).get_context_data(**kwargs)
       # To use in the template
       context['recaptcha_challenge_secret'] = settings.G_RECAPTCHA_CHALLENGE_SECRET
       return context

forms.py

import requests

from django.contrib.auth.forms import AuthenticationForm
from django.conf import settings
from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _


class MyAuthenticationForm(AuthenticationForm):
    def __init__(self, *args, **kwargs):
        self.g_recaptcha_response = kwargs.pop('g-recaptcha-response', None)
        self.remote_ip = kwargs.pop('remote_ip', None)
        super(MyAuthenticationForm, self).__init__(*args, **kwargs)

    def clean(self):
        cleaned_data = super(MyAuthenticationForm, self).clean()
        self.verify_captcha()
        return cleaned_data

    def verify_captcha(self):
        if self.g_recaptcha_response:
            data = {
                'secret': settings.G_RECAPTCHA_VERIFY_SECRET,
                'response': self.g_recaptcha_response,
                'remoteip': self.remote_ip
            }

            response = requests.post(settings.G_RECAPTCHA_VERIFICATION_URL, data=data)
            result = response.json()

            if result['success']:
                return

        raise ValidationError(
            _('Invalid reCAPTCHA challenge.'),
            code='invalid_recaptcha_challenge'
        )

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接