如何在 Django 中验证 Google reCAPTCHA v2

How to validate Google reCAPTCHA v2 in django

我一直在尝试在我制作的网站上使用 Google reCAPTCHA。验证码加载到网页上,但我无法使用多种方法对其进行验证。我已经使用给出的方法尝试了 recaptcha 验证 How to use Python plugin reCaptcha client for validation? 但我认为它已经过时了,因为它不再有效,它指的是挑战,而我正在尝试使用的是 Google 的新 'checkbox' reCAPTCHA v2 或者我可能需要在安装 recaptcha-client 或 django-recaptcha 后更改我的设置。

请帮忙!

这里有一个第三方 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>

这是一个简单的例子,使用 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)

Google 改变了 API 左右,我们现在需要使用 POST 请求。如果您需要在多个 django 视图中进行验证,这里有一个 re-usable 解决方案:

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

然后:

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 设置文件中!

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. 

扩展@trinchet 给出的答案,这里是对 FormView Django class 的简单修改,以自动处理 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 }}作为网站密钥)。

我是这样处理提议的问题的:

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'
        )