这年头如果会上网而不知道验证码为何物的同学,估计也算极品了,即便不知为何物,见也该见过了。如果确实没有见过,没关系,文章后面会有个实例。

验证码(CAPTCHA),文艺一点的说法是:“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机和人的公共全自动程序。现实一点的说法就是,防止程序自动提交页面上的表单,发送乱七八糟内容(广告)的机制。

一般网站上对验证码的使用,无非就是随机生成图片,在服务端存储图片上的文字内容,待用户提交表单的时候比对图片内容和用户填写的是否一致,如果是,就认为这次表单提交是人为有效的,pass之,如果不是,就拒绝该请求。

话说这玩意对普通用户来讲挺无聊的不是?浪费了服务器资源,浪费了眼力,有那时间可以多看几行代码和肥皂剧……

后来reCAPTCHA就登场了。reCAPTCHA是美国卡内基梅隆大学(CMU)设计的,一个借助于人类大脑对难以识别的字符的辨别能力,进行对古旧书籍中难以被光学文字辨识软件(OCR)识别的字符进行辨别的系统。这个系统的原理就是生成一个包含两个词的验证码,其中一个词是OCR无法辨别的字,另一个是已经知道正确答案的字。如果使用者正确的回答出已知正确答案的字,则系统假设另一个字是被认真的辨识过后输入的,一般情况下都是正确的。这样就通过用户的帮助识别了OCR无法辨识的词。也就是说,reCAPTCHA不仅可以反Spam,而且同时还可以帮助进行古籍的数字化工作(可以称为人工OCR)。

reCAPTCHA于09年的时候被Google收购,现在仍然是一项公开免费的服务,只需要一个Google账户即可以将它集成到你的网站,详细可以参考reCAPTCHA官方文档

螺壳网目前就使用了reCAPTCHA服务,包括文章评论、留言反馈、登录等表单提交时均能看到。在瓷器镇使用Google服务的麻烦就是你不知道啥时候就被墙了,即使不被墙,网络也不会很稳定。螺壳网使用reCAPTCHA的时候前端设计了延迟加载,后端也考虑替代方案,当reCAPTCHA不可用的时候可以自己生成验证码代替。供参考的前端代码实现如下:

var f = $('#form-remark');
var r = document.createElement('script'); r.type = 'text/javascript'; r.async = true;
r.src = "http://www.google.com/recaptcha/api/js/recaptcha_ajax.js";
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(r, s);

var w = 0;
var a = function(){
    console.log('Waiting for Google Recaptcha: ' + w + 'ms');
    if (window.Recaptcha)
    {
        Recaptcha.create('你的Google reCAPTCHA公钥', 'recaptcha', {
            callback: function(){
                $('iframe:hidden').hide();Recaptcha.focus_response_field();
                f.find('button[type=submit]').removeAttr('disabled');
            }
        });
    }
    else if (w >= 1500)
    {
        // 加载超时,使用网站自身验证码
    }
    else
    {
        setTimeout(a, w += 500);
    }
}; a();

上述代码只有在用户聚焦到表单或者点击提交的时候才会执行,减少不必要的验证码加载请求,同时也可以自定义验证码的显示样式。

螺壳网目前使用的效果如图:

而对于服务端的认证,reCAPTCHA本身提供了多种语言实现的库,包括Python、PHP、Java和ASP.NET等,可以很方便的连接Google服务器验证用户的输入。例如Python服务端,引入recaptcha client库以后,使用如下示例代码就可以验证输入:

from recaptcha.client.captcha import submit

if submit(request.input('recaptcha_challenge_field'), request.input('recaptcha_response_field'), 'Google reCAPTCHA Private Key', request.remote_ip).is_valid:
    print True
else:
    print False