图形验证码是项目开发过程中经常遇到的一个功能,在很多语言中都有对应的不同形式的图形验证码功能的封装,python 中同样也有类似的封装操作,通过绘制生成一个指定的图形数据,让前端HTML页面通过链接获取到对应的图片验证码进行操作。

  • 什么是验证码?

验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序

  • 验证码的作用?

可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。

验证码自从2002年提出以来,证明了它的效果后,在互联网上得到了迅速的推广。在发展过程中,出现了图形验证码,语言验证码,邮件验证码,短信验证码等等。但是它们的原理大抵相同。

  • 验证码原理!

首先验证码是一个程序概念,它通过向请求的发起方提出问题,能正确回答的即使人类,反之则为机器。这个程序基于这个样一个重要的假设:提出的问题要容易被人类解答,机器无法解答。在当时的技术条件下,识别扭曲的图形,对于机器来说还是一个很艰难的任务,对于人来说,相对可以接受。所以最开始的验证码是图形验证码,也是比较容易实现的验证码。

  • 图形验证码的工作流程

我们登录,注册时首先会向服务器发送一个页面请求。服务器在接到这个请求后,随机生成一个字符串,然后将这个字符串画成一张图片,并将这个图片返回给请求用户。用户在收到这个页面之后再次提交请求数据时,需要识别这张图片上的字符,并且填写跟需要提交的数据一并提交给服务器。服务器在收到这些数据后,会首先判断图片上的字符串跟之前生成的字符串是否一致,一致则说明提交合法,反之不合法。

那么我们今天通过python中的常用的web框架tornado来实现一个图形验证码。通过tornado搭建一个web服务器是非常容易的。下面的代码就是一个通过tornado实现的web服务器。

核心操作步骤:

  1. 生成图形验证码【封装】
  2. HTML页面请求【验证码】
  3. tornado handler中进行处理

1. 生成图形验证码

这里我们通过PIL模块的图形绘制操作完成核心的验证码 功能

首先安装PIL模块:

> pip install PIL

很遗憾,上面的命令执行不会成功,PIL库是Pillow图像库的一部分,已经迁移了地址,执行如下命令进行安装:

> easy_install Pillow

其次,开发图形验证码的函数封装(VerifyCode.py):

在初始化数据函数中,通过font_type指定了具体的字体文件名称,在使用时要将对应的corbel.ttf字体文件拷贝到VerifyCode.py同级目录下

  • VerifyCode.py文件
#coding:utf-8

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="corbel.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance = 2):
    '''
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    '''

    width, height = size # 宽, 高
    img = Image.new(mode, size, bg_color) # 创建图形
    draw = ImageDraw.Draw(img) # 创建画笔

    def get_chars():
        '''生成给定长度的字符串,返回列表格式'''
        return random.sample(chars, length)

    def create_lines():
        '''绘制干扰线'''
        line_num = random.randint(*n_line) # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            #结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        '''绘制干扰点'''
        chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        '''绘制验证码字符'''
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                    strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)

    return img, strs

2. server.py文件

# -*- coding:utf-8 -*-

import tornado.web
import tornado.ioloop
import tornado.httpserver
import io
import VerifyCode

#定义一个路由类
class indexHandler(tornado.web.RequestHandler):
    #添加一个处理get请求方式的方法
    def get(self):
        #向响应中添加数据
        # self.write('hello,tornado,my name is get...')
        self.render('index.html')
    def post(self, *args, **kwargs):
        # self.write('hello tornado my name is post...')
        username=self.get_argument('user')
        password=self.get_argument('pwd')
        ':
            self.write('登录成功')
        else:
            self.write('用户密码错误')

class CheckCodeHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        #创建一个文件流
        imgio=io.BytesIO()
        #生成图片对象和对应字符串
        img,code=VerifyCode.create_validate_code()
        #将图片信息保存到文件流
        img.save(imgio,'GIF')
        #返回图片
        self.write(imgio.getvalue())

if __name__ == '__main__':
    # 创建一个APP,并注册路由
    app=tornado.web.Application([(r'/index',indexHandler),
                                 (r'/check_code',CheckCodeHandler),
                                 ])
    #监听端口,HTTP服务
    http_server=tornado.httpserver.HTTPServer(app)
    #原始方式
    http_server.bind(8888)
    http_server.start(1)
    # 监听本地端口8888
    #http_server.listen(8888)
    # 启动服务
    tornado.ioloop.IOLoop.current().start()

3. 页面index.html文件

后端程序已经准备完成,接下来的操作,就是在前端网页中,通过img标签来加载对应的图形验证码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        #imgCode{width: 100px;height: 40px;border: 1px solid red;}
    </style>
</head>
<body>
<form action="/index" method="post" enctype="multipart/form-data">
    <p><input name="user" type="text" placeholder="用户"></p>
    <p><input name="pwd" type="password" placeholder="密码"></p>
    <p><input name="code" type="text" placeholder="验证码">
        ![](/check_code)
    </p>
    <p><input type="submit" value="submit"></p>

</form>
<script>

    function ChangeCode() {
        var code=document.getElementById('imgCode');
        // 每次更新get地址,防止出现get请求缓存问题
        code.src="/check_code?rd=" + new Date()
    }
</script>
</body>
</html>

4. 运行程序,查看结果

打开浏览器,访问指定的登录页面,验证码如下图所示


5.详解


这段是路由规则,r”/index”是规则,支持正则表达式。这条路由代表,url为“/index”的请求指向IndexHandler。那么我们在浏览器中访问127.0.0.1:8080/index的时候,浏览器的请求就会交给IndexHandler来响应。


业务处理模块,也是我们开发工作的核心。每一个类对应一个业务功能,所有的类必须继承tornado.web.RequestHandler类,这个类是tornado中用来处理请求的类。上面讲到浏览器访问”/index”,这个请求会被路由转发到IndexHandler类。因为是get请求,所以会执行get方法。Self.write(‘hello world!’),会返回‘hello world!’字符串。所以浏览器也会接收到这个字符串。

首先我们需要在服务器端写一个登录的html文件。


为了简单,直接将这个文件命名为index.html放到当前目录。我们需要修改get方法中的代码。


self.render(‘index.html’)会返回‘index.html’页面

在index.html中form表单会向action指向的url发送post请求。


post请求的url是”/index”,所以我们需要在IndexHandler中再写一个post方法,来处理登录。


Self.get_argument(‘user’)可以获取post请求中发过来的数据,参数user对应html中form标签里的元素的name。

 

那么我们今天需要添加一个图形验证码的功能。首先需要修改前端页面如下:

 

Tornado框架实现图形验证码功能的更多相关文章

  1. SpringSceurity(3)---图形验证码功能实现

    SpringSceurity(3)---图形验证码功能实现 有关springSceurity之前有写过两篇文章: 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSec ...

  2. spring boot:spring security给用户登录增加自动登录及图形验证码功能(spring boot 2.3.1)

    一,图形验证码的用途? 1,什么是图形验证码? 验证码(CAPTCHA)是"Completely Automated Public Turing test to tell Computers ...

  3. SpringSecurity实现图形验证码功能

    ⒈封装验证码类 package cn.coreqi.security.validate; import java.awt.image.BufferedImage; import java.time.L ...

  4. 一百一十五:CMS系统之实现点击更换图形验证码功能

    把验证码渲染到到页面上 访问,显然,是标签有个内边距 去掉内边距 加一个class 如果放大看的话,还有问题 用js实现点击更换图形验证码:生成查询字符串的形式访问图形验证码接口的url,放到img标 ...

  5. Python之tornado框架实现翻页功能

    1.结果如图所示,这里将html页面与网站的请求处理放在不同地方了 start.py代码 import tornado.ioloop import tornado.web from controlle ...

  6. gin框架中图形验证码的生成和验证

    功能和验证码使用原理 本案例中没有使用redis作为缓存,而是使用的内存存储方法 github链接地址 下载命令 go get github.com/mojocn/base64Captcha 请求处理 ...

  7. Django学习笔记(17)——BBS+Blog项目开发(1)验证码功能的实现

    本文主要学习验证码功能的实现,为了项目BBS+Blog项目打下基础. 为了防止机器人频繁登陆网站或者破坏分子恶意登陆,很多用户登录和注册系统都提供了图形验证码功能. 验证码(CAPTCHA)是“Com ...

  8. SpringSceurity(4)---短信验证码功能实现

    SpringSceurity(4)---短信验证码功能实现 有关SpringSceurity系列之前有写文章 1.SpringSecurity(1)---认证+授权代码实现 2.SpringSecur ...

  9. 第二百七十节,Tornado框架-生成验证码图片,以及验证码结合Session验证

    Tornado框架-生成验证码图片,以及验证码结合Session验证 第一.生成验证码图片  生成验证码图片需要两个必须模块 1.python自带的random(随机模块) 2.Pillow()图像处 ...

随机推荐

  1. 基于hashchange导航管理

    想在五一放假的时候写出来,由于放假有点兴奋,心早就跑了,不废话了. 说一下基于hashchange导航管理: 浏览器的历史记录导航是用户非常常用的功能,除了点击前进后退按钮外,Window上的hist ...

  2. window 8.1 + python 3.6 + chrome 59 + selenium 3.4 环境配置

    系统环境 window 8.1 python 3.6 (已经安装了pip) chrome 59.0.3071.115 步骤 安装selenium pip install selenium 下载chro ...

  3. <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步(1)

    <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步 并发问题是所有问题的基础,也是操作系统设计的基础.并发包括很多设计问题,其中有进程间通信,资源共享与竞争,多个 ...

  4. 记录一下Maven整合spring,hibernate,strusts2我程序中出的bug

    action类如下 package com.itheima.movenweb.action; import java.util.List; import org.apache.struts2.Serv ...

  5. Activiti初学问题,求解

    <userTask id="writeReportTask" name="Write monthly financial report" > < ...

  6. Effective Java 第三版——39. 注解优于命名模式

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  7. No plugin found for prefix 'tomcat' in the current project and in the plugin groups和java.net.BindException: Address already in use: JVM_Bind <null>:8080的错误解决

    错误报告:No plugin found for prefix 'tomcat' in the current project and in the plugin groups [org.apache ...

  8. 关于Resin SSL支持的两个问题

    1.Resin的OpenSLL支持功能只有收费Pro版才支持,这一点,只有你做好配置,测试的时候才会在提示中发现,文档里没有说明. 2.它的官方文档这部分有问题,第一个问题就是上面第一条没有说,第二个 ...

  9. 基于one2team框架的Highcharts图表图片导出方案

    这篇文章已经没有什么意义了,新版的HIghcharts提供Java图片导出解决方案,你需要做的就是下个Maven,bulid一个war就Ok了.---addedy on 2012-11-15 多说一句 ...

  10. Nginx日志配置及配置调试

    防火墙内的内网服务器,因为网关传过来的remot_addr都一样,不得不对Nginx的日志格式做了配置 配置语法如下: log_format  myformat  '$http_x_forwarded ...