目录
templates/login/login.html
utils/code.py
views/login.py
验证码
生成验证码
code.py
应用验证码
views.py
login.html
templates/login/login.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'css/bootstrap.css'%}">
</head>
<body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
html {
height: 100%;
}
body {
height: 100%;
}
.container {
height: 100%;
width: 100%;
background-image: linear-gradient(to right, #fbc2eb, #a6c1ee);
}
.login-wrapper {
background-color: #fff;
width: 358px;
height: 588px;
border-radius: 15px;
padding: 0 50px;
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.header {
font-size: 38px;
font-weight: bold;
text-align: center;
line-height: 200px;
}
.input-item {
display: block;
width: 100%;
margin-bottom: 20px;
border: 0;
padding: 10px;
border-bottom: 1px solid rgb(128, 125, 125);
font-size: 15px;
outline: none;
}
.input-item:placeholder {
text-transform: uppercase;
}
.btn {
text-align: center;
padding: 10px;
width: 100%;
margin-top: 40px;
background-image: linear-gradient(to right, #a6c1ee, #fbc2eb);
color: #fff;
}
.msg {
text-align: center;
line-height: 88px;
}
a {
text-decoration-line: none;
color: #abc1ee;
}
</style>
</head>
<body>
<div class="container">
<div class="login-wrapper">
<div class="header">Login</div>
<div class="form-wrapper">
<form method="post" novalidate>
{% csrf_token %}
<div class="col-md-12">
{{ form.username }}
<span style="color: red">{{ form.password.errors.0 }}</span>
{{ form.password }}
</div>
<div class="col-md-7">
{{ form.code }}
<span style="color:red;">{{ form.code.errors.0 }}</span>
</div>
<div class="col-md-5">
<button style="border: none">
<img src="/image/code/">
</button>
</div>
<button class="btn" type="submit">Login</button>
</form>
</div>
</div>
</div>
</body>
</html>
</body>
</html>
utils/code.py
# -*- coding:utf-8 -*-
# pip install pillow==9.4.0
from PIL import Image, ImageFont, ImageDraw
from random import choice,randint
def create_image_content():
# 创建一张图片
img = Image.new(mode="RGB", size=(110, 40), color=(255, 255, 255))
# 创建一个画笔
draw = ImageDraw.Draw(img, mode="RGB")
font = ImageFont.truetype("./simkai.ttf", size=30)
# 验证码出现的字符
text = "ABCDEFG23456789"
# 存放四位验证码的字符串
image_text = ""
# 生成几位数的验证码,就循环几次
for num in range(4):
image_text += choice(text)
# 每次遍历的时候,将字符添加到图片上
x = 15
for i in image_text:
# 为每一位验证码添加不同颜色
R = str(randint(0, 255))
G = str(randint(0, 255))
B = str(randint(0, 255))
draw.text(
(x, 5),
text=i,
font=font,
fill=f"rgb({R},{G},{B})"
)
x += 20
# 添加干扰线条
for i in range(1, randint(3, 6)):
x1, y1 = randint(0, 110), randint(0, 40)
x2, y2 = randint(0, 110), randint(0, 40)
R = str(randint(0, 255))
G = str(randint(0, 255))
B = str(randint(0, 255))
draw.line((x1, y1, x2, y2),fill=f"rgb({R},{G},{B})", width=2)
# 添加干扰点
for i in range(1, randint(30, 50)):
x1, y1 = randint(0, 110), randint(0, 40)
R = str(randint(0, 255))
G = str(randint(0, 255))
B = str(randint(0, 255))
draw.point([x1, y1], fill=f"rgb({R},{G},{B})")
# print(image_text)
# img.save("code.png")
# print(img)
return [img, image_text]
views/login.py
# -*- coding:utf-8 -*-
from django.shortcuts import render, redirect,HttpResponse
from demo_one.utils import pwd_data
from django import forms
from demo_one import models
from demo_one.utils.code import create_image_content
class LoginForm(forms.Form):
username = forms.CharField(label="用户名", widget=forms.TextInput(
attrs={"class": "input-item", "autocomplete": "off", "placeholder": "请输入用户名"}))
password = forms.CharField(label="密码", widget=forms.PasswordInput(
attrs={"class": "input-item", "autocomplete": "off", "placeholder": "请输入密码"}))
code = forms.CharField(label="验证码", widget=forms.TextInput(
attrs={"class": "input-item", "autocomplete": "off", "placeholder": "请输入验证码"}))
def clean_password(self):
pwd = self.cleaned_data.get("password")
# print(self.cleaned_data)
return pwd_data.md5(pwd)
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login/login.html", {"form": form})
form = LoginForm(data=request.POST)
if form.is_valid():
# 去数据库进行校验
# print(form.cleaned_data)
# 使用pop删除,pop返回被删除的值
user_input_code = form.cleaned_data.pop("code")
code = str(request.session.get("image_code"))
# print("我自己输入的是:", user_input_code)
# print("系统生成的是:", code)
if user_input_code.upper() != code.upper():
form.add_error("code", "验证码错误")
return render(request, "login/login.html", {"form": form})
admin_object = models.Adminrole.objects.filter(**form.cleaned_data).first()
if not admin_object:
# 给输入框添加一个错误提示
form.add_error("password", "用户名或密码错误")
return render(request, "login/login.html", {"form": form})
# 登录成功之后
# 将登录信息存储在session当中
request.session["info"] = {"id": admin_object.id, "username": admin_object.username,
"password": admin_object.password, "role": admin_object.role}
# 时效性
request.session.set_expiry(60 * 60 * 24 * 30)
# 登录成功后跳转到首页
return redirect("/")
return render(request, "login/login.html", {"form": form})
def logout(request):
request.session.clear()
return redirect("/login/")
from io import BytesIO
def image_code(request):
image, text = create_image_content()
request.session["image_code"] = text
request.session.set_expiry(60)
stream = BytesIO()
image.save(stream, "png")
return HttpResponse(stream.getvalue())
验证码
生成验证码
-
code.py
# -*- coding:utf-8 -*- # pip install pillow==9.4.0 from PIL import Image, ImageFont, ImageDraw from random import choice,randint def create_image_content(): # 创建一张图片 img = Image.new(mode="RGB", size=(110, 40), color=(255, 255, 255)) # 创建一个画笔 draw = ImageDraw.Draw(img, mode="RGB") font = ImageFont.truetype("./simkai.ttf", size=30) # 验证码出现的字符 text = "ABCDEFG23456789" # 存放四位验证码的字符串 image_text = "" # 生成几位数的验证码,就循环几次 for num in range(4): image_text += choice(text) # 每次遍历的时候,将字符添加到图片上 x = 15 for i in image_text: # 为每一位验证码添加不同颜色 R = str(randint(0, 255)) G = str(randint(0, 255)) B = str(randint(0, 255)) draw.text( (x, 5), text=i, font=font, fill=f"rgb({R},{G},{B})" ) x += 20 # 添加干扰线条 for i in range(1, randint(3, 6)): x1, y1 = randint(0, 110), randint(0, 40) x2, y2 = randint(0, 110), randint(0, 40) R = str(randint(0, 255)) G = str(randint(0, 255)) B = str(randint(0, 255)) draw.line((x1, y1, x2, y2),fill=f"rgb({R},{G},{B})", width=2) # 添加干扰点 for i in range(1, randint(30, 50)): x1, y1 = randint(0, 110), randint(0, 40) R = str(randint(0, 255)) G = str(randint(0, 255)) B = str(randint(0, 255)) draw.point([x1, y1], fill=f"rgb({R},{G},{B})") # print(image_text) # img.save("code.png") # print(img) return [img, image_text]
应用验证码
-
views.py
def login(request): if request.method == "GET": form = LoginForm() return render(request, "login/login.html", {"form": form}) form = LoginForm(data=request.POST) if form.is_valid(): # 去数据库进行校验 # print(form.cleaned_data) # 使用pop删除,pop返回被删除的值 user_input_code = form.cleaned_data.pop("code") code = str(request.session.get("image_code")) # print("我自己输入的是:", user_input_code) # print("系统生成的是:", code) if user_input_code.upper() != code.upper(): form.add_error("code", "验证码错误") return render(request, "login/login.html", {"form": form}) admin_object = models.Adminrole.objects.filter(**form.cleaned_data).first() if not admin_object: # 给输入框添加一个错误提示 form.add_error("password", "用户名或密码错误") return render(request, "login/login.html", {"form": form}) # 登录成功之后 # 将登录信息存储在session当中 request.session["info"] = {"id": admin_object.id, "username": admin_object.username, "password": admin_object.password, "role": admin_object.role} # 时效性 request.session.set_expiry(60 * 60 * 24 * 30) # 登录成功后跳转到首页 return redirect("/") return render(request, "login/login.html", {"form": form}) def logout(request): request.session.clear() return redirect("/login/") from io import BytesIO def image_code(request): image, text = create_image_content() request.session["image_code"] = text request.session.set_expiry(60) stream = BytesIO() image.save(stream, "png") return HttpResponse(stream.getvalue())
login.html
-
login.html {% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="{% static 'css/bootstrap.css'%}"> </head> <body> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> * { margin: 0; padding: 0; } html { height: 100%; } body { height: 100%; } .container { height: 100%; width: 100%; background-image: linear-gradient(to right, #fbc2eb, #a6c1ee); } .login-wrapper { background-color: #fff; width: 358px; height: 588px; border-radius: 15px; padding: 0 50px; position: relative; left: 50%; top: 50%; transform: translate(-50%, -50%); } .header { font-size: 38px; font-weight: bold; text-align: center; line-height: 200px; } .input-item { display: block; width: 100%; margin-bottom: 20px; border: 0; padding: 10px; border-bottom: 1px solid rgb(128, 125, 125); font-size: 15px; outline: none; } .input-item:placeholder { text-transform: uppercase; } .btn { text-align: center; padding: 10px; width: 100%; margin-top: 40px; background-image: linear-gradient(to right, #a6c1ee, #fbc2eb); color: #fff; } .msg { text-align: center; line-height: 88px; } a { text-decoration-line: none; color: #abc1ee; } </style> </head> <body> <div class="container"> <div class="login-wrapper"> <div class="header">Login</div> <div class="form-wrapper"> <form method="post" novalidate> {% csrf_token %} <div class="col-md-12"> {{ form.username }} <span style="color: red">{{ form.password.errors.0 }}</span> {{ form.password }} </div> <div class="col-md-7"> {{ form.code }} <span style="color:red;">{{ form.code.errors.0 }}</span> </div> <div class="col-md-5"> <button style="border: none"> <img src="/image/code/"> </button> </div> <button class="btn" type="submit">Login</button> </form> </div> </div> </div> </body> </html> </body> </html>