P56图片验证码(校验)
将生成的动态验证码存入session中:
所有用户都访问同一个url生成动态验证码,需要给不同用户存到不同的位置
def image_code(request):
from app03_phone_number_management.utils.code import check_code
img, code_string = check_code()
# 将突变验证码写入到session,设置60s过期
request.session['image_code'] = code_string
request.session.set_expiry(60)
print(code_string)
from io import BytesIO
stream = BytesIO()
img.save(stream, 'png')
return HttpResponse(stream.getvalue())
LoginForm类重写:
class LoginForm(forms.Form):
username = forms.CharField(
label="用户名",
widget=forms.TextInput(attrs={'class': 'form-control'}),
required=True,
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(
attrs={'class': 'form-control'},
render_value=True,
),
required=True,
)
code = forms.CharField(
label="验证码",
widget=forms.TextInput(
attrs={'class': 'form-control'},
),
required=True,
)
# 在attr中向前端添加class样式,也可以多继承
def clean_password(self):
pwd = self.cleaned_data.get("password")
from app03_phone_number_management.utils.encrypt import md5
return md5(pwd)
对应login前端的Form自动生成验证码的输入框
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-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>
{% csrf_token %}
<div class="form-group">
<label for="exampleInputEmail">用户名</label>
{# <input type="text" class="form-control" placeholder="用户名">#}
{{ form.username }}
<span style="color: red">{{ form.username.errors.0 }}</span>
</div>
<div class="form-group">
<label>密码</label>
{# <input type="password" class="form-control" placeholder="密码">#}
{{ 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">
{{ 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>
登录逻辑加入验证码校验逻辑:
!!!一定要把session超时时间改回来
def login(request):
if request.method == 'GET':
form = LoginForm()
return render(request, 'login.html', {'form': form})
# else:
form = LoginForm(data=request.POST)
if form.is_valid():
# 校验验证码
# 使用pop方法的原因:在数据库中检验没有code字段,校验完要剔除
user_input_code = form.cleaned_data.pop('code')
code = request.session.get('session', "")
if code.upper() != user_input_code.upper():
form.add_error('code', "验证码错误")
return render(request, 'login.html', {'form': form})
# test
# print(form.cleaned_data)
# return HttpResponse('提交成功')
# 验证校验:
# admin_object = models.Admin.objects.filter(username=form.cleaned_data['username'], password=form.cleaned_data['password']).first()
# 定义类时的字段名要和数据库中的一致
admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
if not admin_object:
form.add_error("password", "用户名或密码错误")
return render(request, 'login.html', {'form': form})
# else用户名正确:
request.session['info'] = {
'id': admin_object.id,
'name': admin_object.username,
}
# 7天免登录
request.session.set_expiry(60*60*24*7)
return redirect('/admin_list/')
# else:
return render(request, 'login.html', {'form': form})