1、引入字体(不必要)
注:字体可以在浏览器上搜索ttf进行下载
2、创建code.py
import random
from PIL import Image,ImageDraw,ImageFont,ImageFilter
def check_code(width=120,height=30,char_length=5,font_file='kai.ttf',font_size=28):
# kai.ttf自己下载字体的文件名
code=[]
img=Image.new(mode="RGB",size=(width,height),color=(255,255,255))
draw=ImageDraw.Draw(img,mode="RGB")
def rndChar():
"生成随机字母"
return chr(random.randint(65,90))
# 随机生成数字
# return str(random.randint(0,9))
def rndColor():
"生成随机颜色"
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):
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)
3、创建url
path('image/code/',account.image_code),
4、在中间件中设置(排除登陆和验证码的情况)
if request.path_info in ["/login/","/image/code/"]:
return
5、html前端页面
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<style>
.account {
width: 400px;
border: 1px solid #dddddd;
border-radius: 5px;
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>
{# novalidate去掉自身所带的错误提示没填东西直接提交#}
{# 使用post请求需要加上#}
{% csrf_token %}
<div class="form-group">
<label>用户名</label>
{{ form.username }}
<span style="color: red">{{ form.username.errors.0 }}</span>
</div>
<div class="form-group">
<label>密码</label>
{{ form.password }}
<span style="color: red">{{ form.password.errors.0 }}</span>
</div>
<div class="form-group">
<label for="id_code">图片验证码</label>
<div class="row">
<div class="col-xs-7">
{# <input type="text" name="code" class="form-control" placeholder="请输入图片验证码" required="" id="id_code">#}
{{ form.code }}
<span style="color: red;">{{ form.code.errors.0 }}</span>
</div>
<div class="col-xs-5">
<img id="image_code" src="/image/code/" style="width: 125px;">
</div>
</div>
</div>
<input type="submit" value="登 陆" class="btn btn-primary">
</form>
</div>
</body>
</html>
5、类中添加code(生成验证码)
class LoginForm(BootStrapForm):
username = forms.CharField(
label="用户名",
widget=forms.TextInput,
# 设置为必填属性
required=True
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(render_value=True),
required=True
)
code = forms.CharField(
label="验证码",
widget=forms.TextInput,
required=True
)
def clean_password(self):
pwd = self.cleaned_data.get("password")
return md5(pwd)
6、显示验证码
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设置60s超时
request.session.set_expiry(60)
# # 图片字母
# print(code_string)
# 新建一个内存,图片存入其中
stream = BytesIO()
img.save(stream, 'png')
stream.getvalue()
return HttpResponse(stream.getvalue())
7、进行校验
def login(request):
# 有一条数据username=202117020035,password=202117020035的数据
# passward=md5("202117020035")
# models.Admin.objects.create(username="202117020035",password=passward)
if request.method == "GET":
form = LoginForm()
return render(request, 'login.html', {"form": form})
form = LoginForm(data=request.POST)
if form.is_valid():
# 验证码的校验
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})
admain_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admain_object:
# 在password字段下显示
form.add_error("password", "用户名或密码错误")
return render(request, 'login.html', {'form': form})
# 用户名和密码都正确
# 网站生成随机字符串;写到用户浏览器的cookie;在写入到session中
request.session["info"] = {'id': admain_object.id, 'name': admain_object.username}
# 七天免登录,session保存七天
request.session.set_expiry(60*60*24*7)
return redirect("/admain/list/")
return render(request, 'login.html', {'form': form})