美文网首页
Flask生成验证码(中英文都存在)

Flask生成验证码(中英文都存在)

作者: 星星在线 | 来源:发表于2020-02-29 11:53 被阅读0次

    main.py

    # -*- coding: utf-8 -*-
    
    from flask import render_template, redirect, url_for, session, flash, make_response
    from . import auth
    from app import db, utils
    
    @auth.route('/VerifyCode/')
    def get_verify_code():
        #把strs发给前端,或者在后台使用session保存
        #code_img, code_text = utils.generate_verification_code()
        code_img, code_text = utils.generate_verification_cn_code()
        session['code_text'] = code_text
        response = make_response(code_img)
        response.headers['Content-Type'] = 'image/jpeg'
        return response
    
    @auth.route('/register', methods=['GET', 'POST'])
    def register():
        form = RegisterForm()
        if form.validate_on_submit():
            username = form.username.data
            if 'code_text' in session and session['code_text'] != form.verification_code.data:
                flash('输入错误:%s' % form.verification_code.data)
            else:
                flash('输入验证码正确')
        return render_template('auth/register.html', form=form)
    
    

    utils_en.py 生成普通验证码

    # -*- coding: utf-8 -*-
    import random
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    from io import BytesIO
    
    _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))
    fontType = "Arial.ttf"
    
    
    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=fontType,
                             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: 验证码字体,默认为 Arial.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)  # 创建画笔
        if draw_lines:
            create_lines(draw, n_line, width, height)
        if draw_points:
            create_points(draw, point_chance, width, height)
        strs = create_strs(draw, chars, length, font_type, font_size, width, height, fg_color)
    
        # 图形扭曲参数
        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
    
    
    def create_lines(draw, n_line, width, height):
        '''绘制干扰线'''
        line_num = random.randint(n_line[0], n_line[1])  # 干扰线条数
        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, width), random.randint(0, height))
            # 结束点
            end = (random.randint(0, width), random.randint(0, height))
            draw.line([begin, end], fill=(0, 0, 0))
    
    
    def create_points(draw, point_chance, width, height):
        '''绘制干扰点'''
        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(draw, chars, length, font_type, font_size, width, height, fg_color):
        '''绘制验证码字符'''
        '''生成给定长度的字符串,返回列表格式'''
        c_chars = random.sample(chars, length)
        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)
    
    #################################
    #返回验证码数据流和字符
    def generate_verification_code():
        code_img, str_text = create_validate_code()
        buf = BytesIO()
        code_img.save(buf, 'JPEG', quality=70)
        buf_str = buf.getvalue()
        return buf_str, str_text
    

    utils_cn.py中文验证码

    import random
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    from io import BytesIO
    import codecs
    
    class RandomChar():
      """用于随机生成汉字对应的Unicode字符"""
      @staticmethod
      def Unicode():
        val = random.randint(0x4E00, 0x9FBB)
        return chr(val)
    
      @staticmethod
      def GB2312():
        head = random.randint(0xB0, 0xCF)
        body = random.randint(0xA, 0xF)
        tail = random.randint(0, 0xF)
        val = ( head << 8 ) | (body << 4) | tail
        str = "%x" % val
        #
        return codecs.decode(str, 'hex_codec').decode('gb2312','ignore')
    
    #默认字体为宋体,大多数系统都存在这种字体
    class ImageChar():
      def __init__(self, fontColor = (0, 0, 0),
                         size = (100, 40),
                         fontPath = 'Songti.ttc',
                         bgColor = (255, 255, 255, 255),
                         fontSize = 20):
        self.size = size
        self.fontPath = fontPath
        self.bgColor = bgColor
        self.fontSize = fontSize
        self.fontColor = fontColor
        self.font = ImageFont.truetype(self.fontPath, self.fontSize)
        self.image = Image.new('RGBA', size, bgColor)
    
      def rotate(self):
        img1 = self.image.rotate(random.randint(-5, 5), expand=0)#默认为0,表示剪裁掉伸到画板外面的部分
        img = Image.new('RGBA',img1.size,(255,)*4)
        self.image = Image.composite(img1,img,img1)
    
      def drawText(self, pos, txt, fill):
        draw = ImageDraw.Draw(self.image)
        draw.text(pos, txt, font=self.font, fill=fill)
        del draw
    
      def randRGB(self):
        return (random.randint(0, 255),
               random.randint(0, 255),
               random.randint(0, 255))
    
      def randPoint(self):
        (width, height) = self.size
        return (random.randint(0, width), random.randint(0, height))
    
      def randLine(self, num):
        draw = ImageDraw.Draw(self.image)
        for i in range(0, num):
          draw.line([self.randPoint(), self.randPoint()], self.randRGB())
        del draw
    
      def randChinese(self, num):
        gap = 0
        start = 0
        strRes = ''
        for i in range(0, num):
          char = RandomChar().GB2312()
          strRes += char
          x = start + self.fontSize * i + random.randint(0, gap) + gap * i
          self.drawText((x, random.randint(-5, 5)), char, (0,0,0))
          self.rotate()
        print(strRes)
        self.randLine(8)
        return strRes,self.image
    
    #################################
    #返回验证码数据流和字符
    def generate_verification_cn_code():
        ic = ImageChar(fontColor=(100,211, 90))
        str_text,code_img = ic.randChinese(4)
        buf = BytesIO()
        if code_img.mode != "RGB":
            code_img = code_img.convert("RGB")
       
        code_img.save(buf,'JPEG',quality=70)
        buf_str = buf.getvalue()
        return buf_str, str_text
    

    模板

    模板内采用了bootstrap-wtf中的quick_form自动生成表单,但是我们要对表单进行一些修改

    {% extends "base.html" %}
    {% import "bootstrap/wtf.html" as wtf %}
    {% block title %}博客注册{% endblock %}
    
    {% block scripts %}
    {{ super() }}
    <script>
        //修改提交按钮继承的类,按钮会变成蓝色
        $("#submit").attr("class", "btn btn-info");
        //修改提交按钮属性,上部外边界和宽度
        $("#submit").css({
            "width":"100%",
            "margin-bottom":"10px"
            });
        //增加一个img元素在验证码标签内,因为这些标签是quick_form自动生成的,所以我们要采用动态添加的方式
        function create_verify_code()
        {
            var img = $('<img id="verify_img" src="{{ url_for('auth.get_verify_code', _external=True) }}">');
            $("#verification_code").before(img);
        }
        //开始创建验证码图片
        create_verify_code();
        $(document).ready(function(){
            //点击验证码刷新,主要采取重设src属性,属性每次都要不一样,所以在网址后面增加参数
            $("img").click(function(){
                var img_url = "{{ url_for('auth.get_verify_code', _external=True) }}?d=" + Math.random();
                $(this).attr("src", img_url);
            });
        });
    </script>
    {% endblock %}
    {% block page_content %}
    <div class="col-md-4 col-md-offset-4">
    {{ wtf.quick_form(form) }}
    <div class="clear"></div>
    <div class="register">
        <a type="button" class="btn btn-info" style="width:100%;" href="{{ url_for('auth.login') }}">已有账号?登录</a>
    </div>
        </div>
    {% endblock %}
    

    效果图如下:


    登录结果显示

    相关文章

      网友评论

          本文标题:Flask生成验证码(中英文都存在)

          本文链接:https://www.haomeiwen.com/subject/aftphhtx.html