2008年7月18日

有备无患:在 Django 上使用 reCAPTCHA 生成验证码

reCAPTCHA 是由 CAPTCHA 的原作者,也是人类计算学(Human Computation)的专家 Luis von Ahn 所设计一个免费的 anti-bot 服务,它在随机提取的单词上加上一些扭曲和可阻碍电脑自动识别的噪声,以利用人类远强于电脑的图形识别能力来检查屏幕前坐着的是人类,还是 bot 或自动脚本。因此,reCAPTCHA 经常被用于生成网站注册或者文章评论的验证码。

除此之外,reCAPTCHA 还有一项非常公益的设计。虽然 OCR (全称 Optical Character Recognitio,中译光学字元识别)早已被投入使用,但是对那些印刷模糊或分辨度不足的实体书籍而言,OCR 的识别率难说完美。而 reCAPTCHA 将这些上无法由电脑自动识别的单词扫描下来,以图片的形式存入数据库,并在网络上以验证码(CAPTCHA)的形式交由人类来破译,而破译的结果将被用于帮助世界上的实体书加快数字化的进程。

你可能会问,既然电脑无法识别这些单词,那么 reCAPTCHA 系统又怎么知道用户填写的是正确的验证码呢?reCAPTCHA 的网站上有详细的解释

一个不能被 OCR 正确识别的新单词,总是会和另一个已知答案的单词一起提交给用户。接下来用户需要同时识别这两个单词。如果用户正确识别出其中已知答案的那个单词,系统即假设用户所识别出的未知答案的新单词也可能是正确的。如果其他的用户也识别出相同的答案,系统则逐步提高这种可能性。最终当这种可能性超过某个阈值时,系统就可以将其认作已知答案的单词了。

言归正传,reCAPTCHA 有丰富的 API,包括对 PHP、JAVA、Ruby,自然还有 Python 的支持。如果要在 Django 上使用 reCAPTCHA,我们可以从 PyPI 下载 reCAPTCHA 的 Python 客户端,或者直接用 easy_install 安装。


# easy_install recaptcha-client

不知道为什么,虽然安装成功了,但是我不能导入 recaptcha 模块(wyt:import captcha 或 import recaptcha 都没有用,哪位如果顺利导入过的话,能不能分享一下你的经验呢?先谢了),所以我是把 egg 中的脚本直接复制到我的 Django 项目目录下了。

接下来,我们需要在 reCAPTCHA 注册并申请一对公钥/私钥,并将其保存在 settings.py 中。

settings.py

1 # reCAPTCHA keys
2 RECAPTCHA_PUBLIC_KEY = "6LdqgAIAAAUSHDmo4IIBmsjUsduAUMUoBDZc3J_T"
3 RECAPTCHA_PRIVATE_KEY = "6LdqgAIAAbjsJKLbj2KOjPO6D4isfJ_AzLSO_256"

然后,我们要为注册表单添加验证码了。在 Django Snippets 上,oggy 已经将 reCAPTCHA 抽象成一个 RecaptchaForm 类,所以我们只要让注册表单(Registration Form)继承这个类,就可以为注册表单添加验证码的功能了。由于 Django newform 默认是根据 field 的定义顺序来生成表单,所以在继承 RecaptchaForm 的时候,也应该把它放在最后继承。

recaptcha/forms.py

01 from django import newforms as forms
02 from django.newforms import ValidationError
03 from django.conf import settings
04 from recaptcha import captcha
05 
06 
07 class RecaptchaWidget(forms.Widget):
08     """ A Widget which "renders" the output of captcha.displayhtml """
09     def render(self, *args, **kwargs):
10         return captcha.displayhtml(settings.RECAPTCHA_PUBLIC_KEY)
11 
12 class DummyWidget(forms.Widget):
13     """
14     A dummy Widget class for a placeholder input field which will
15     be created by captcha.displayhtml
16 
17     """
18     # make sure that labels are not displayed either
19     is_hidden=True
20     def render(self, *args, **kwargs):
21         return ''
22 
23 class RecaptchaForm(forms.Form):
24     """
25     A form class which uses reCAPTCHA for user validation.
26    
27     If the captcha is not guessed correctly, a ValidationError is raised
28     for the appropriate field
29     """
30     recaptcha_challenge_field = forms.CharField(widget=DummyWidget)
31     recaptcha_response_field = forms.CharField(widget=RecaptchaWidget, label='')
32 
33     def __init__(self, request, *args, **kwargs):
34         super(RecaptchaForm, self).__init__(*args, **kwargs)
35         self._request = request
36 
37     def clean_recaptcha_response_field(self):
38         if 'recaptcha_challenge_field' in self.cleaned_data:
39             self.validate_captcha()
40         return self.cleaned_data['recaptcha_response_field']
41 
42     def clean_recaptcha_challenge_field(self):
43         if 'recaptcha_response_field' in self.cleaned_data:
44             self.validate_captcha()
45         return self.cleaned_data['recaptcha_challenge_field']
46 
47     def validate_captcha(self):
48         rcf = self.cleaned_data['recaptcha_challenge_field']
49         rrf = self.cleaned_data['recaptcha_response_field']
50         ip_address = self._request.META['REMOTE_ADDR']
51         check = captcha.submit(rcf, rrf, settings.RECAPTCHA_PRIVATE_KEY, ip_address)
52         if not check.is_valid:
53             raise ValidationError('You have not entered the correct words')


user/forms.py

01 from myproject.recaptcha.forms import RecaptchaForm
02 
03 class RegistrationForm(forms.Form):
04     """
05     Form for registering a new user account.
06      
07     """
08 
09 class RegistrationFormWithRecaptcha(RegistrationForm, RecaptchaForm):
10     """
11     Subclass of ``RegistrationForm`` and ``RecaptchaForm`` which can tell whether its user
12     is a human or a computer.
13 
14     """

2 评论:

燕子乡 说...

我很想用,但是我是新学着用django的表单处理的,博主能不能说说说怎么在一个已有的django项目里集成这个东西呢?

wyt 说...

@燕子乡
就像上面说的,让你的注册表单继承RecaptchaForm就好了

发表评论

欢迎留言

订阅我的博客

搜索我的博客

正在加载...

我的豆瓣广播

分享阅读

豆瓣秀

休斯敦火箭

我的文章归档

版权申明