這部分爲Flask博客的登陸頁面加個驗證碼。使用了PIL模塊生成驗證碼圖片,並經過Flask的session機制,進行驗證碼驗證。html
一、生成驗證碼前端
使用string模塊:string.ascii_letters+string.digits構造了驗證碼字符組合。使用的PIL模塊,構建了圖形對象,並進行劃線和高斯模糊處理。字體文件可單獨保存到工程裏。繪製字符串時,draw.text的前兩個參數爲字符的位置,能夠設置爲隨機數,使驗證碼各字符的位置不固定,而且相鄰字符略有重合。get_verify_code返回了圖形對象和字符串。git
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
二、表單類flask
爲LoginForm增長一個verify_code字段,用來輸入驗證碼。cookie
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')
三、視圖函數 session
使用io.BytesIO對象將驗證碼圖片轉化爲二進制形式,直接做爲響應返回前端。設置首部字段的內容格式,這樣二進制的內容就能以圖形形式在頁面中顯示。驗證碼字符串保存在flask.session對象中,對session的操做就像處理字典同樣。程序內部使用設置中的SECRET_KEY對session數據加密後,存儲在cookie中。app
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中保存的驗證碼字符串都轉換成小寫後再做判斷。dom
@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)
四、前端函數
在前端中加入了驗證碼圖形的路徑,該路徑指定爲生成圖形響應的視圖函數。當點擊驗證碼圖片時,驗證碼會予以更新。字體
{{ wtf.quick_form(form) }} <img class="verify_code" src="/auth/code " onclick="this.src='/auth/code?'+ Math.random()">
調整下佈置,最終登陸表單會顯示成這個樣子:
下面是不一樣方式生成的驗證碼:
a. 無特效 | b. 增長高斯模糊 | c. 增長劃線 |