среда, 3 сентября 2014 г.

Keycaptcha для Django

Есть такой замечательный сервис www.keycaptcha.com, с помощью которого можно пристроить на сайт каптчу, которая не пробивается подавляющим большинством парка спам-ботов (в т.ч. с использованием antigate и аналогов).

Для python имеется простой и понятный модуль, добавляющий соответствующий функционал. Я его и использовал в своем django-проекте. Но если прикручивать keycaptcha к существующим проектам на django, где используются большие развесистые формы, то его использование будет выглядеть как чужеродная вставка. Хотелось бы просто добавить в определение django-формы поле keycaptcha и получить нужный функционал, не внося больше никаких изменений в существующий код.

Для этого имеющийся модуль python был доработан для использования в django. Использовать его можно как написано выше, с одним нюансом. При инстанцировании формы с каптчей, конструктору формы нужно передать значение по умолчанию для поля каптчи. Этим значением должен быть IP адрес, с которого пришел обрабатываемый запрос.

Т.е. Если форма обьявлена вот так:

 >>> import keycaptcha  
 >>>   
 >>> class MyForm(forms.Form):  
 >>>   keycaptcha = keycaptcha.Field()  

То создавать ее нужно вот так:

 >>> form = MyForm(initial={'keycaptcha': request.META['REMOTE_ADDR']})  

Проверка каптчи происходит стандартно в методе формы .is_valid(). У keycaptcha имеется свой пример прикручивания к django, но какой то странный и требующий много телодвижений. templatetags зачем то туда приплели. Имхо мой вариант симпатишнее.

keycaptcha.py

 """  
 Support keycaptcha (www.keycaptcha.com) in Django   
 (C) 2014 by Vitaly Bogomolov mail@vitaly-bogomolov.ru  
 Based at code: https://www.keycaptcha.ru/python-captcha-api/  
   
 Usage:  
   
 >>> from django import forms  
 >>> import keycaptcha  
 >>>   
 >>> class MyForm(forms.Form):  
 >>>   keycaptcha = keycaptcha.Field()  
 >>>   
 >>> ...  
 >>>   
 >>> form = MyForm(initial={'keycaptcha': request.META['REMOTE_ADDR']})  
 >>>   
 >>> ...  
 >>>   
 >>> if request.method == 'POST':  
 >>>   form = MyForm(request.POST)  
 >>>   if form.is_valid():  
 >>> ...  
   
 Template:  
   
 <form action="" method="POST">   
  {% csrf_token %}  
  {{myform.as_p}}  
  <input type="submit" id="postbut" value="Test" />  
 </form>  
   
 """  
   
 from random import randint  
 from hashlib import md5  
 from urllib import urlopen  
   
 # Replace PUT_YOUR_PRIVATE_KEY_HERE and PUT_YOUR_KEYCAPTCHA_USER_ID_HERE  
 # with proper values from your keycaptcha.com account  
 KeyCAPTCHA_PrivateKey = 'XXXXXXXXXXXXXXXXX'  
 KeyCAPTCHA_UserID = 'XXXXX'  
   
 KeyCAPTCHA_Template = '''<!-- KeyCAPTCHA code (www.keycaptcha.com)-->  
 <input id="capcode" type="hidden" name="%s" value="123" />  
 <script language="JavaScript">  
   var s_s_c_user_id = '#kc_user_id#';  
   var s_s_c_session_id = '#kc_session_id#';  
   var s_s_c_captcha_field_id = 'capcode';  
   var s_s_c_submit_button_id = 'postbut';  
   var s_s_c_web_server_sign = '#kc_s1#';  
   var s_s_c_web_server_sign2 = '#kc_s2#';  
 </script>  
 <script language=JavaScript src="https://backs.keycaptcha.com/swfs/cap.js"></script>  
 <!-- end of KeyCAPTCHA code-->  
 '''  
   
 def show_keycaptcha(remote_ip='127.0.0.1') :  
   session_id = str(randint(100000000,999999999999))  
   s1 = md5( session_id + remote_ip + KeyCAPTCHA_PrivateKey ).hexdigest()  
   s2 = md5( session_id + KeyCAPTCHA_PrivateKey ).hexdigest()  
   rd = { 'kc_user_id':KeyCAPTCHA_UserID, 'kc_s1':s1, 'kc_s2':s2, 'kc_session_id':session_id }  
   st = KeyCAPTCHA_Template  
   for k in rd.keys() :  
     st = st.replace( '#' + k + '#', rd[k] )  
   return st  
   
 def validate_keycaptcha( capcode ) :   
   cap = capcode.split('|')  
   if len( cap ) < 3 : return False  
   valid_cap_sign = md5( 'accept' + cap[1] + KeyCAPTCHA_PrivateKey + cap[2] ).hexdigest()  
   if valid_cap_sign <> cap[0] : return False  
   if cap[2].find( 'http://' ) == 0 :   
     try :  
       f = urlopen( cap[2] + cap[1] )  
       st = f.read()  
       f.close()  
     except :  
       return False  
     if st <> '1' : return False  
   else : return False  
   return True  
   
 ##################  
 ### Django wrapper  
   
 from django import forms  
 from django.utils.safestring import mark_safe  
   
 class Widget(forms.HiddenInput):  
   
   def render(self, name, value, attrs=None):  
     return mark_safe(show_keycaptcha(remote_ip=value) % name)  
   
 class Field(forms.CharField):  
   
   def __init__(self):  
     super(Field, self).__init__(widget=Widget())  
    
   def validate(self, value):  
     super(Field, self).validate(value)  
     if not validate_keycaptcha(value):  
       raise forms.ValidationError("Wrong keycaptcha")  
   
   

Комментариев нет:

Отправить комментарий