一、用pillpw生成图片验证码
1、安装pillow
pip install pip install pillow
2、下载字体
比如:Monaco.ttf
3、实现生成验证码的方法
该方法返回一个img ,可以把这个img图片保存到内存中,也可以以文件形式保存到磁盘,还返回了验证码的文字。
在app01->utils->code.py, code.py内容如下:
import random
from PIL import Image,ImageDraw,ImageFont,ImageFilter
def check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28):
code = []
img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
def rndChar():
"""
生成随机字母
:return:
"""
return chr(random.randint(65, 90))
def rndColor():
"""
生成随机颜色
:return:
"""
return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))
# 写文字
font = ImageFont.truetype(font_file, font_size)
for i in range(char_length):
char = rndChar()
code.append(char)
h = random.randint(0, 4)
draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
# 写干扰点
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
# 写干扰圆圈
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())
# 画干扰线
for i in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=rndColor())
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
return img, ''.join(code)
验证码的效果如下:
二、在登录界面加上验证码
1、添加一个验证码的url, 在urls.py 中增加:
from django.urls import path
from app01.views import user,depart,pretty,admin,account
urlpatterns = [
path('login/account/',account.login),
path('logout/',account.logout),
path('image/code/',account.image_code),
]
2、在视图函数中增加验证码的处理
把生成后的图片保存再内存中,返回给页面,验证码的文字要先保存到session中,方便后面根用户输入的验证码进行校验。
from django.shortcuts import render,redirect,HttpResponse
from django import forms
from io import BytesIO
from app01.utils.code import check_code
def image_code(request):
#调用pillow模块生成验证码
img,code_string = check_code()
#将验证码写入到自己的session中,一遍后续获取验证码进行校验
request.session['image_code'] = code_string
#给session设置60秒超时
request.session.set_expiry(60)
#将图片内容写入到内存中,from io import BytesIO
stream = BytesIO()
img.save(stream, 'png')
return HttpResponse(stream.getvalue())
3、在登录页面增加验证码的输入框
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}">
<style>
.account {
width: 400px;
border-radius: 5px;
border: 1px solid #dddddd;
box-shadow: 5px 5px 20px #aaa;
margin-left: auto;
margin-right: auto;
margin-top: 100px;
padding: 20px 40px;
}
.account h2 {
margin-top: 10px;
text-align: center;
}
</style>
</head>
<body>
<div class="account">
<h2>用户登录</h2>
<form method="post" novalidate>
{% csrf_token %}
<div class="form-group">
<label >{{ form.username.label }}</label>
{{ form.username}}
<span style="color:red">{{ form.username.errors.0 }}</span>
</div>
<div class="form-group">
<label >{{ form.passwd.label }}</label>
{{ form.passwd }}
<span style="color:red">{{ form.passwd.errors.0 }}</span>
</div>
<div class="form-group">
<label for="id_code">图片验证码</label>
<div class="row">
<div class="col-xs-7">
{{ form.code }}
<span style="color: red">{{ form.code.errors.0 }}</span>
</div>
<div class="col-xs-5">
<img id="image_code" src="/image/code/">
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">登录</button>
</form>
</div>
<script src="{% static 'js/jquery-3.7.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>
登录界面效果如下:
3、登录的时候对验证码进行校验
获取到用户输入的验证码的值,再从session中取出之前保存的验证码的值,两者比较,如果一致就登录,不一致就重定向到登录界面,提示验证码错误。
还要注意的是,session在生成验证码的时间设置了过期时间是60s, 在登录的时候需要重写设置session的有效期。
在登录的视图函数修改如下:
def login(request):
"""用户登录"""
if request.method == 'GET':
form = LoginForm()
return render(request,'login.html',{'form':form})
form = LoginForm(data=request.POST)
if form.is_valid():
#验证码的校验
# 这里为什么用pop 是因为cleaned_data获取到的是用户输入的所有信息,包括验证码,但是后面用户名密码根数据库校验的时候验证码是不在数据库的,所以这里就把验证码给去掉
user_input_code = form.cleaned_data.pop('code')
code = request.session.get('image_code',"")
if code.upper() != user_input_code.upper():
form.add_error("code","验证码错误")
return render(request,'login.html',{'form':form})
#print(form.cleaned_data) #获取到的值是一个字典{'username': 'root', 'passwd': '4233c0d596c55f18df8c99ad1ad8af4f'}
#校验数据库的用户名和密码
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
form.add_error('passwd','用户名或密码错误')
return render(request,'login.html',{'form':form})
#用户名和密码正确
#网站生成随机字符串; 写到用户浏览器的cookie中,再写入到session中
request.session['info'] = {'id':admin_object.id,'name':admin_object.username}
#重新设置session 的有效期,为24小时
request.session.set_expiry(60*60*24)
return redirect('/admin/list/')
return render(request,'login.html',{'form':form})