1、前言
最近要用python生成验证码,在网上搜索了以后大部分都是使用的python
的PIL库
的,但是大部分就是将生成的验证码图片保存到本地,然后在前端使用如下格式显示;
![](验证码图片的路径)
补充一下环境是win10、python2.7
2、生成验证码
关于生成验证码可以参照廖大的http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00140767171357714f87a053a824ffd811d98a83b58ec13000
或者这个http://www.jianshu.com/p/7b39db561e75
我是使用后者的代码修改自用的。
注意:如何安装依赖的库就不说了,主要是在运行过程可能会遇到如下错误
(1)ImportError
File "D:\Python26\Lib\site-packages\PIL\ImageFont.py", line 34, in __getattr__
raise ImportError("The _imagingft C module is not installed")
解决方法是卸载PIL安装pillow
pip install PIL
pip install pillow
(2)无法加载字体
IOError: cannot open resource
解决方法是将win10字体的绝对地址给出C:\Windows\Fonts\Arial.ttf
3、问题
看了几篇博文写的都是先生成验证码图片,然后在在前端显示,由于验证码很小,没必要保存到本地,这样可以省掉一个页面上的HTTP请求,坏处就是浏览器不能缓冲验证码图片,其实我们没必要要浏览器来缓冲它,因为验证码具有变化性。所以我们在HTML页面上直接从内存中读取验证码图片。
有一些网页上有些图片后面跟着一大串符号如
data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAYAAABIdFAMAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAH
JREFUeNo8zjsOxCAMBFB/
KEAUFFR0Cbng3nQPw68ArZdAlOZppPFIBhH5EAB8b+Tlt9MYQ6i1BuqFaq1CKSVcxZ2Acs6406KUgpt5/
LCKuVgz5BDCSb13ZO99ZOdcZGvt4mJjzMVKqcha68iIePB86GAiOv
CDADlIUQBs7MD3wAAAABJRU5Er
这些是Data URI Scheme,目的是将一些小数据,直接嵌入到网页中,而不从外部文件加载。
所以我们可以将生成的验证码文件编码之后发送到前端。
4、所有代码
(1)、生成验证码(代码来自搬运)
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from webapp.config import DevConfig
try:
import cStringIO as StringIO
except ImportError:
import StringIO
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母
_upper_cases = "ABCDEFGHJKLMNPQRSTUVWXY" # 大写字母
_numbers = "1234567890" # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers)) # 生成允许的字符集合
default_font = DevConfig.FONTPATH # 验证码字体,在配置文件中定义
# 生成验证码接口120 30
def generate_verify_image(size=(140, 40),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type=default_font,
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance=2,
save_img=False):
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 xrange(width):
for h in xrange(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) # 滤镜
mstream = StringIO.StringIO()
img.save(mstream, img_type)
return mstream.getvalue().encode('base64'), strs
(2)、视图
@auth_blueprint.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
# 验证码不区分大小写
if 'code_text' in session and session['code_text'] != form.verification_code.data.lower():
flash(u'验证码错误!')
image_stream, code_text = generate_verify_image()
session['code_text'] = code_text.lower()
return render_template('login.html', form=form, image_stream=image_stream)
user = User.query.filter_by(username=form.user_name.data).first()
if user is not None and user.check_password(form.password.data):
login_user(user, form.remember_me.data)
session['user_name'] = user.username
identity_changed.send(
current_user._get_current_object(),
identity=Identity(user.id)
)
return redirect(url_for('main.home'))
flash(u"用户名或密码错误")
image_stream, code_text = generate_verify_image()
session['code_text'] = code_text.lower()
return render_template('login.html', form=form, image_stream=image_stream)
(3)、前端
<form action="" method="post" class="form" role="form">
{{ form.hidden_tag() }}
<div class="form-group required">
{{ form.user_name.label(class="control-label") | safe }}
{{ form.user_name(class="form-control", required=True) }}
</div>
<div class="form-group required">
{{ form.password.label(class="control-label") | safe }}
{{ form.password(class="form-control", required=True) }}
</div>
{% if form.errors.verification_code %}
<div class="form-group has-error required">
{{ form.verification_code.label(class="control-label") | safe }}
{{ form.verification_code(class="form-control", required=True) }}
{% for error in form.errors.verification_code %}
<p class="help-block">{{ error }}</p>
{% endfor %}
</div>
{% else %}
<div class="form-group required">
{{ form.verification_code.label(class="control-label") | safe }}
<label class="control-label"><img
src="data:image/gif;base64,{{ image_stream }}"></label>
{{ form.verification_code(class="form-control", required=True) }}
</div>
{% endif %}
<div class="form-group">
{{ form.remember_me.label(class="control-label") }}
{{ form.remember_me }}
</div>
{{ form.submit(class="btn btn-info submit") }}
</form>
END
网友评论