这部分为 Flask 博客的登录页面加个验证码. 使用了 PIL 模块生成验证码图片, 并通过 Flask 的 session 机制, 进行验证码验证.
1, 生成验证码
使用 string 模块: string.ascii_letters+string.digits 构造了验证码字符组合. 使用的 PIL 模块, 构建了图形对象, 并进行划线和高斯模糊处理. 字体文件可单独保存到工程里. 绘制字符串时, draw.text 的前两个参数为字符的位置, 可以设置为随机数, 使验证码各字符的位置不固定, 并且相邻字符略有重合. get_verify_code 返回了图形对象和字符串.
- import random
- import string
- from PIL import Image, ImageFont, ImageDraw, ImageFilter
- def rndColor():
- '''随机颜色'''
- return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))
- def gene_text():
- '''生成 4 位验证码'''
- return ''.join(random.sample(string.ascii_letters+string.digits, 4))
- def draw_lines(draw, num, width, height):
- '''划线'''
- for num in range(num):
- x1 = random.randint(0, width / 2)
- y1 = random.randint(0, height / 2)
- x2 = random.randint(0, width)
- y2 = random.randint(height / 2, height)
- draw.line(((x1, y1), (x2, y2)), fill='black', width=1)
- def get_verify_code():
- '''生成验证码图形'''
- code = gene_text()
- # 图片大小 120×50
- width, height = 120, 50
- # 新图片对象
- im = Image.new('RGB',(width, height),'white')
- # 字体
- font = ImageFont.truetype('app/static/arial.ttf', 40)
- # draw 对象
- draw = ImageDraw.Draw(im)
- # 绘制字符串
- for item in range(4):
- draw.text((5+random.randint(-3,3)+23*item, 5+random.randint(-3,3)),
- text=code[item], fill=rndColor(),font=font )
- # 划线
- draw_lines(draw, 2, width, height)
- # 高斯模糊
- im = im.filter(ImageFilter.GaussianBlur(radius=1.5))
- return im, code
2, 表单类
为 LoginForm 增加一个 verify_code 字段, 用来输入验证码.
- class LoginForm(FlaskForm):
- email = StringField('Email', validators=[DataRequired(), Length(1, 64),
- Email()],)
- password = PasswordField('Password', validators=[DataRequired()])
- verify_code = StringField('VerifyCode', validators=[DataRequired()])
- remember_me = BooleanField('Keep me logged in')
- submit = SubmitField('Log In')
3, 视图函数
使用 io.BytesIO 对象将验证码图片转化为二进制形式, 直接作为响应返回前端. 设置首部字段的内容格式, 这样二进制的内容就能以图形形式在页面中显示. 验证码字符串保存在 flask.session 对象中, 对 session 的操作就像处理字典一样. 程序内部使用设置中的 SECRET_KEY 对 session 数据加密后, 存储在 cookie 中.
- from io import BytesIO
- @auth.route('/code')
- def get_code():
- image, code = get_verify_code()
- # 图片以二进制形式写入
- buf = BytesIO()
- image.save(buf, 'jpeg')
- buf_str = buf.getvalue()
- # 把 buf_str 作为 response 返回前端, 并设置首部字段
- response = make_response(buf_str)
- response.headers['Content-Type'] = 'image/gif'
- # 将验证码字符串储存在 session 中
- session['image'] = code
- return response
在登录的视图函数中, 添加验证码验证功能. 注意一般验证码是不区分大小写的, 这里将输入的验证码和 session 中保存的验证码字符串都转换成小写后再作判断.
- @auth.route('/login', methods=['GET', 'POST'])
- def login():
- form = LoginForm()
- if form.validate_on_submit():
- user = User.query.filter_by(email=form.email.data).first()
- if session.get('image').lower() != form.verify_code.data.lower():
- flash('Wrong verify code.')
- return render_template('auth/login.html', form=form)
- if user is not None and user.verify_password(form.password.data):
- login_user(user, form.remember_me.data)
- return redirect(request.args.get('next') or url_for('main.index'))
- flash('Invalid username or password.')
- return render_template('auth/login.html', form=form)
4, 前端
在前端中加入了验证码图形的路径, 该路径指定为生成图形响应的视图函数. 当点击验证码图片时, 验证码会予以更新.
{{ wtf.quick_form(form) }}
<img class="verify_code" src="/auth/code" onclick="this.src='/auth/code?'+ Math.random()">
调整下布置, 最终登录表单会显示成这个样子:
下面是不同方式生成的验证码:
a. 无特效 | b. 增加高斯模糊 | c. 增加划线 |
来源: https://www.cnblogs.com/ik-heu/p/8840518.html