首先展示一下最后的效果图
这就是我们一个验证码效果图
安装环境:
这一步主要使用的模块是 pillow,没有安装的话可以使用 pip install pillow 进行安装
PIL:Python Image Library,是 Python 处理图片的标准库,不过 PIL 仅支持到 Python2.7,之后有人在其基础上创建了兼容的版本,名字就叫做 pillow。
1.先来创建验证码图片
#产生验证码图片
from random import randint
from PIL import Image, ImageDraw, ImageFont
def get_random_color():
# 随机颜色RGB
return randint(120, 200), randint(120, 200), randint(120, 200)
def get_random_code():
# 随机字符
codes = [[chr(i) for i in range(48, 58)], [chr(i) for i in range(65, 91)], [chr(i) for i in range(97, 123)]]
codes = codes[randint(0, 2)]
return codes[randint(0, len(codes)-1)]
def generate_captcha(width=140, height=60, length=4):
# 生成验证码
img = Image.new("RGB", (width, height), (250, 250, 250))
draw = ImageDraw.Draw(img)
#font.ttf 为字体文件
font = ImageFont.truetype(os.path.dirname(__file__)+ '/font.ttf', size=36)
# 验证码文本
text = ""
for i in range(length):
c = get_random_code()
text += c
rand_len = randint(-5, 5)
draw.text((width * 0.2 * (i+1) + rand_len, height * 0.2 + rand_len), c, font=font, fill=get_random_color())
# 加入干扰线
for i in range(3):
x1 = randint(0, width)
y1 = randint(0, height)
x2 = randint(0, width)
y2 = randint(0, height)
draw.line((x1, y1, x2, y2), fill=get_random_color())
# 加入干扰点
for i in range(16):
draw.point((randint(0, width), randint(0, height)), fill=get_random_color())
basedir = os.path.abspath(os.path.dirname(__file__))+ '/code/'
#生成一个MD5加密名字,以防被传到前端被爬取
md_text = hashlib.md5('{}zanzanzanzanzanzan'.format(text).encode()).hexdigest()
#保存图片
img.save(basedir + md_text + ".jpg")
#text为生成的验证码,md_text为生成的文件名称
return text,md_text
2.调用创建图片接口
sched模块实现了一个时间调度程序,该程序可以通过单线程执行来处理按照时间尺度进行调度的时间。
通过调用scheduler.enter(delay,priority,func,args)函数,可以将一个任务添加到任务队列里面,当指定的时间到了,就会执行任务(func函数)
import time, sched
import datetime
#调用验证码图片生成接口
@admin_views.route('/get_captcha')
def get_captcha():
#调用生辰图片接口返回:验证码,以及文件名
text,md_text = generate_captcha()
#拼接生成的验证码图片名
img= os.path.join('/code/',md_text+'.jpg')
#这里我把我们刚获得的验证码存在了session里,作为全局,比较安全也比较试用
session['code'] = text
#这一步的意思是,我去进程的方式开一个任务不影响后续操作
t1 = multiprocessing.Process(target=print_some_times)
t1.start()
# print_some_times()
return img
#定时任务,定时来删除创建的图片
s = sched.scheduler(time.time, time.sleep)
def print_time():
#产生图片的目录
basedir = os.path.abspath(os.path.dirname(__file__))+ '/code/'
#os.listdir 以列表形式展示当前文件夹下的图片
list_ = os.listdir(basedir)
#遍历当前文件夹下的文件
for i in list_:
join_export_path = basedir + i
# 获取创建时间
establish_time = time.strftime('%M ',time.localtime(os.path.getctime(join_export_path)))
# 获取当前时间
current_time = time.strftime('%M ',time.localtime(time.time()))
# M代表分钟 创建时间-当前时间 不等于0
#相当于只存在60秒
if int(current_time) - int(establish_time) !=0:
#删除当前文件
os.remove(join_export_path)
#把session的值也清空,可以简单代替验证码的时效性
session['code'] = '验证码过期'
def print_some_times():
print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
s.enter(60,1,print_time)#任务的间隔时间,任务被调度到相同的时间执行,要执行的任务函数
s.run()
print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
3.前端界面
这里我使用的前端框架为layui,当然你可以使用自己喜欢框架
layui官网(感兴趣可以看一看):https://www.layui.com/
<html class="x-admin-sm">
<head><meta charset="UTF-8"></head>
<div class="login layui-anim layui-anim-up">
<body>
<div class="message">管理员登录</div>
<div id="darkbannerwrap"></div>
<form class="layui-form" method="post">
<input type="hidden" id="err" value="{{err}}">
<input name="user_name" id="user_name" placeholder="用户名" type="text" lay-verify="required" class="layui-input" value="">
<hr class="hr15">
<input name="user_passwd" id="user_passwd" lay-verify="required" placeholder="密码" type="password" class="layui-input" value="">
<hr class="hr15">
<table>
<tr>
<td>
<input type="text" name='your_in' id="your_in" title="请输入验证码" placeholder="验证码(不区分大小写)" class="layui-input"/>
</td>
<td>
<a href="javascript:void(0)" onclick="Change()"><img src="" id="captcha"></a><br>
<a href="javascript:void(0)" onclick="Change()">看不清楚,换一张</a><br>
</td>
</tr>
</table>
<hr class="hr15">
<input value="登录" lay-submit lay-filter="login" style="width:100%;" type="button" id="button">
<hr class="hr20" >
</form>
</div></div></div>
</body>
<script>
//验证码
function Change() {
$.ajax({
url: '{{ url_for('admin_views.get_captcha') }}',
async: true,
type: "GET",
success: function (data) {
document.getElementById("captcha").src = data;
}
})
}
//监听回车键提交from表单
$('#your_in').on('keydown', function (event) {
if (event.keyCode == 13) {
layui.use('form', function(){
var form = layui.form;
$.ajax({
url:'/api/user/login/', //调用验证接口
method:'post',
data:{
'user_name':$( "input[name='user_name']").val(),
'user_passwd':$( "input[name='user_passwd']").val(),
'code':$( "input[name='your_in']").val(),
},
//请求的页面响应成功,则进行处理:
success: function(res) {
var data = JSON.parse(res);
if (data.code ==200) {
x = $.cookie('token');
layer.msg('登录成功', {
icon: 1,
time: 2000
},
function() {
if(document.referrer !='/'){
location.href = '/index/'
}
else{
location.href = '/index/';}
var fun ='go'
collect.fu(fun)
return false;
});
} else {
data = JSON.parse(res)
layer.msg( data.msg);
return false;
}
},
//请求的页面响应失败,则进行处理:
error: function(data) {
layer.msg(JSON.stringify(data.field),
function() {
location.reload();
});
return false;
},
})
});dataType="json"
}
});
window.onload = Change();
</script>
</html>
4.后端账号密码 验证
@api_views.route('/api/user/login/', methods=['POST'])
def user_login():
code = request.form.get('code').upper()
if session['code'] == '验证码过期':
return api_result(code=-1, msg='验证码已过期,请从新输入')
if code != session['code'].upper():
return api_result(code=-1, msg='验证码错误')
sql_dic = {
'user_name': request.form.get("user_name"),
'user_passwd': str_md5(request.form.get("user_passwd")),
}
现在我们已经获取到账号,密码,验证码
接下来就要到自己的库里去判断该用户名密码是否正确
到这里为止,一个简单的登录验证码主页就做出来了,当然还有很多可以去完善的地方,欢迎各位码农,大佬指出