Flask验证码的使用,运用PIL库生成简单的图形验证码
  • 分类:Python
  • 发表:2019-06-16
  • 围观(137,574)
  • 评论(5)

在我们网上冲浪的时候验证码的存在就像是空气一般的存在,是根本无法避免的。对于完全不了解开发的同学,他们总会嫌弃验证码拖慢他们的工作效率。但是对于我们网站的建设者来说,验证码的存在就像是一道坚固的城墙,将一些恶意行为拒之门外。验证码的存在作为一种人机识别手段,可以简单的对正常人或者机器的操作加以区分。将一些恶意行为从源头阻拦。比如说防止暴力密码破解行为。我记得之前比尔盖茨说过:“任何密码在暴力穷举面前都不堪一击”。验证码的存在可以很好的防范这种行为,初次之外还有恶意灌水、垃圾注册、恶意登录、刷票等行为。这些而已行为会破坏服务的正常运行。同时由于这些恶意操作都是机器提交的,会给网站服务器带来巨大的压力,堪比DDOS攻击,所以验证码的存在是有意义且必要的。

如何在Flask框架内实现简单的图形验证码呢?这个就是我今天研究的重点了😏。首先声明这个思路是我从别人博客借鉴的,代码也是完全照着别人的思路写的,但是学习就是这样取长补短的过程不是么?我们先来说一下验证码实现的思路:大致思路就是通过服务端随机生成验证码图片,并且将验证码内容通过session缓存至客户端,当收到客户端的表单的时候验证发送的验证码数据和session缓存中的数据是否一致来判断是否通过验证。因为session是不不对称加密的,所以不用担心会被破解。还是放张图来体会一下吧:

简易验证码工作流程

图形验证码的生成

验证码实现涉及到图像操作,这就不得不提到Python强大的Pillow库了。通过简单的API实现强大的图像处理,这是对Pillow库的最好总结。通过Pillow库我们可以简单的实现符合我们前端页面风格的图形验证码,比起Java的图形处理真是超级简单省心呢。生成验证码其实很简单,使用Pillow创建一个图像,并将随机生成的字符打印到图像上,最后对图像进行风格化和噪声处理就完成了。先看看我实现的风格。

验证码样式

 

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

class VercCode():
    random_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012345789'
    width = 107
    height = 43

    @classmethod
    def generate_vercode(cls):
        # 创建一个新的图像, 设置长宽和背景颜色
        img = Image.new('RGB', (cls.width, cls.height), "#f1f0f0")
        font = ImageFont.truetype('msyhbd.ttc', 30)
        draw = ImageDraw.Draw(img)
        vercode = ""

        # 生成随机验证码,并将验证码转成图像打印到图像
        for item in range(4):
            code = random.choice(cls.random_letters)
            vercode += code
            draw.text((6 + random.randint(1, 2) + 23 * item, 2), text=code, fill=cls.__random_color(), font=font)

        # 画几条随机线,让验证码看起来更专业
        for x in range(4):
            x1 = random.randint(0, cls.width // 2)
            y1 = random.randint(0, cls.height // 2)
            x2 = random.randint(0, cls.width)
            y2 = random.randint(cls.height // 2, cls.height)
            draw.line(((x1, y1), (x2, y2)), fill=cls.__random_color(), width=2)

        # 加上一层滤波器滤镜
        img = img.filter(ImageFilter.EDGE_ENHANCE)
        return img, vercode.lower()

    @classmethod
    def __random_color(cls):
        # 随机生成一个RGB颜色值
        return tuple([random.randint(64, 180) for _ in range(3)])

在Flask内实现带验证码的表单

对于表单的验证,我们只需要验证传来表单内验证码数据是否于session中缓存的数据一致。如果数据一致的话就证明验证码通过,如果验证不通过的话就执行失败操作。原理很简单,我们需要在Flask工程内添加两个视图函数:一个视图函数用于返回验证码图像信息,另一个视图函数作为验证测试表单。

@web.route("/vercode")
def vercode():
    image, vercode = VercCode.generate_vercode()
    buffer = BytesIO()
    image.save(buffer, "png")
    buffer_str = buffer.getvalue()
    response = make_response(buffer_str)
    response.headers['Content-Type'] = 'image/gif'
    session["vercode"] = vercode
    return response


@web.route("/register", methods=['GET', 'POST'])
def register():
    form = VercodeForm(request.form)
    if form.validate_on_submit():
        true_code = session.get("vercode")
        if form.vercode.data == true:
            flash("验证通过")
            return render_template(success.html", form=form)
        flash("验证码错误.请从新输入")
    return render_template("vercode.html", form=form)

两个视图函数完成之后就只差前端页面的耦合了,在这里前端不是我们重点讨论的对象,就不贴具体代码了。这样我们就实现简单的图像验证码功能了,可以说是十分简单了。可是对于验证码来说这样的安全性似乎并没有提高很多?

关于验证码的思考

上面的验证码我们虽然实现了,从表面看功能似乎齐全,看起来也蛮高大上,实则不然。验证码设计并不是这么简单的,在设计中需要注意许多规范。就好比上面设计的验证码就有一个致命的缺陷,生成的验证码可以无限复用,因为没有给验证码添加使用之后失效的规则。总之问题提出来了,解决的思路还是要后期慢慢研究啊。小小的验证码背后藏着大大的安全问题。Flask-WTF文档说支持验证码校验,但是文档写的太晦涩了我看不懂😅🐷,后面研究一下在写个总结吧,毕竟入门,毕竟菜鸡。

给几个我网上找到的博客文章,看看大神对验证码的实现有哪些思考:http://www.lijiejie.com/safe-issues-of-captcha/[图形验证码的常见安全问题]https://zhuanlan.zhihu.com/p/50433406[如何设计相对安全的图形验证码?]。两个文章总结的还是比较全面的,有时间可以好好研究一下不是?


共有 5 条评论

  1. red-tea

  2. delicate

    可以分享一下全部的代码吗

    1. Avatar photo

      Weiney

      其实这已经是很完整的代码啦,你可以好好看看,这个验证码有很多bug,只是做了个样子出来

  3. delicate

    我刚开始学着做,很多还不是很懂,想跑一遍全部的代码,但是不知道这些代码贴在哪里,所以想看看你全部的代码

    1. Avatar photo

      Weiney

      如果是还在入门的话可以从文档的简单例子开始啊,又不懂的也可以加QQ一起探讨的

Top