链接:https://pan.baidu.com/s/1TJCCZTt9LBtM9enxAY-vlQ?pwd=a4bu
提取码:a4bu
写一个登录页面
from django.urls import path
from app01.views import account
urlpatterns = [
path("login/", account.login, name="login"),
]
#forms.account.py
from django import forms
class LoginForm(forms.Form):
role = forms.ChoiceField(
label="角色",
required=True,
choices=(("2", "客户"), ("1", "管理员"),)
)
username = forms.CharField(
label="用户名",
widget=forms.TextInput
)
passwod = forms.CharField(
label="密码",
widget=forms.PasswordInput
)
#views.account.py
from django.shortcuts import render
from app01.forms.account import LoginForm
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
#html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>用户登录</h3>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<p>
<label>{{ field.label }}</label>
{{ field }}
<span>{{ field.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit" value="提交">
</form>
</body>
</html>
项目的结构:
短信登录&&密码登录跳转
后端代码逻辑类似
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<p>
<label>{{ field.label }}</label>
{{ field }}
<span>{{ field.errors.0 }}</span>
</p>
{% endfor %}
<input type="submit" value="提交">
{#这里用的就是urls.py的别名,name=xxx#}
<a href="{% url 'login' %}">账户登录</a>
</form>
</body>
</html>
用户登录的逻辑
def login(request):
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
# 格式是否正确
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
# 数据库校验,这里要选择表,管理员 || 普通客户
data_dict = form.cleaned_data
role = data_dict.pop("role")
if role == "1":
user_object = models.Administrator.objects.filter(**data_dict).filter(active=1).first()
else:
user_object = models.Customer.objects.filter(**data_dict).filter(active=1).first()
# 数据输入错误
if not user_object:
form.add_error("password", "用户名或密码错误")
return render(request, "login.html", {"form": form})
# 通过校验,存入session
mapping = {"1": "ADMIN", "2": "CUSTOMER"}
request.session['user_info'] = {
"role": mapping[role],
"id": user_object.id,
"name": user_object.username,
}
#成功,跳转到后台路径
from django.conf import settings
#HOME_URL在settings.py中设置 HOME_URL = "/home/"
return redirect(settings.HOME_URL)
#倘若想保留密码,即使输错了
class LoginForm(forms.Form):
role = forms.ChoiceField(
label="角色",
required=True,
choices=(("2", "客户"), ("1", "管理员"),)
)
username = forms.CharField(
label="用户名",
widget=forms.TextInput
)
password = forms.CharField(
label="密码",
#这个是重点
widget=forms.PasswordInput(render_value=True)
)
密码不要存明文,存哈希值
import hashlib
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-&2p7a#%zq6v8y14xr(6(s)8ww3e-)1!)t!=utgqurh_pa0e(k@'
def md5(data_string):
obj = hashlib.md5(SECRET_KEY.encode('utf-8'))
obj.update(data_string.encode('utf-8'))
return obj.hexdigest()
print(md5("1111"))
写在校验的时候
from utils.encrypt import md5
class LoginForm(forms.Form):
role = forms.ChoiceField(
label="角色",
required=True,
choices=(("2", "客户"), ("1", "管理员"),)
)
username = forms.CharField(
label="用户名",
widget=forms.TextInput
)
password = forms.CharField(
label="密码",
widget=forms.PasswordInput(render_value=True)
)
def clean_password(self):
old_password = self.cleaned_data['password']
return md5(old_password)
短信登录的逻辑
发送短信验证码(ajax请求):
填写手机号,发送短信,手机号用ajax发送后台
生成随机数字 + 调用第三方api短信
手机号 + 短信验证码 + redis保存
登录:
填写手机号 + 填写短信验证码
是否失效
获取手机号 + ajax请求流程:
引入jquery,自行搜索
sms_login.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<p>
<label>{{ field.label }}</label>
{{ field }}
<span>{{ field.errors.0 }}</span>
</p>
{% endfor %}
{#绑定一个事件#}
<input id="btnSendSms" type="button" value="点击获取验证码" class="btn btn-default"/>
<input type="submit" value="提交">
{#这里用的就是urls.py的别名,name=xxx#}
<a href="{% url 'login' %}">账户登录</a>
</form>
<script src="{% static 'js/jquery.js' %}"></script>
<script>
$(function () {
bindSendSmsEvent();
})
function bindSendSmsEvent() {
$("#btnSendSms").click(function (){
console.log(123456);
});
}
</script>
</body>
</html>
记得添加路径
获取电话
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" novalidate>
{% csrf_token %}
{% for field in form %}
<p>
<label>{{ field.label }}</label>
{{ field }}
<span>{{ field.errors.0 }}</span>
</p>
{% endfor %}
{#绑定一个事件#}
<input id="btnSendSms" type="button" value="点击获取验证码" class="btn btn-default"/>
<input type="submit" value="提交">
{#这里用的就是urls.py的别名,name=xxx#}
<a href="{% url 'login' %}">账户登录</a>
</form>
<script src="{% static 'js/jquery.js' %}"></script>
<script>
$(function () {
bindSendSmsEvent();
})
function bindSendSmsEvent() {
$("#btnSendSms").click(function (){
$.ajax({
url: "/send/sms/",
type: "POST",
data:{
{#固定方法#}
mobile:$("#id_mobile").val(),
role:$("#id_role").val(),
},
dataType: "JSON",
success: function (res){
console.log(res);
}
})
});
}
</script>
</body>
</html>
我们这里假装发了一个验证码
#forms.form
class SendSmsForm(forms.Form):
role = forms.ChoiceField(
label="角色",
required=True,
choices=(("2", "客户"), ("1", "管理员"),)
)
mobile = forms.CharField(
label="电话",
widget=forms.TextInput,
required=True,
validators=[RegexValidator(r'1[3578]\d{9}$', "手机格式错误")]
)
# 从已注册的用户中获取
def clean_mobile(self):
old_mobile = self.cleaned_data['mobile']
role = self.cleaned_data['role']
if role == "1":
exists = models.Administrator.objects.filter(active=1).filter(mobile=old_mobile).first()
else:
exists = models.Customer.objects.filter(active=1).filter(mobile=old_mobile).first()
if not exists:
raise ValidationError("手机号不存在")
sms_code = str(random.randint(1000, 9999))
print("验证码:", sms_code)
# 调用第三方接口发送验证码,这里没用上,自定义的一个
is_ok = send_sms(old_mobile, sms_code)
if not is_ok:
raise ValidationError("短信发送失败!")
# 短信验证码写入redis,设置超时时间
conn = get_redis_connection("default")
# 60秒有效
conn.set(old_mobile, sms_code, ex=60)
return old_mobile
#views
#@csrf_exempt是django防止csrf攻击,我们去除验证。不然发不了
@csrf_exempt
def send_sms(request):
print(request.POST)
# 校验格式
# 限制次数,一般第三方平台有这个功能
form = SendSmsForm(data=request.POST)
if form.is_valid():
return JsonResponse({"status": True, "msg": "OK"})
else:
return JsonResponse({"status": False, "msg": form.errors})
电话验证码登录
后端校验
def sms_login(request):
if request.method == "GET":
form = SmsLoginForm()
return render(request, "sms_login.html", {"form": form})
form = SmsLoginForm(data=request.POST)
if not form.is_valid():
return JsonResponse({"status": False, "msg": "fail"})
role = form.cleaned_data["role"]
mobile = form.cleaned_data["mobile"]
if role == "1":
user_object = models.Administrator.objects.filter(mobile=mobile).filter(active=1).first()
else:
user_object = models.Customer.objects.filter(mobile=mobile).filter(active=1).first()
if not user_object:
return JsonResponse({"status": False, "msg": {"mobile": ["手机号不存在"]}})
# 通过校验,存入session
mapping = {"1": "ADMIN", "2": "CUSTOMER"}
request.session['user_info'] = {
"role": mapping[role],
"id": user_object.id,
"name": user_object.username,
}
return JsonResponse({"status": True, "msg": "OK", "data":settings.HOME_URL})
实现登录成功,跳转
<form method="post" id="SmsForm" novalidate>
{% csrf_token %}
{% for field in form %}
<p>
<label>{{ field.label }}</label>
{{ field }}
<span>{{ field.errors.0 }}</span>
</p>
{% endfor %}
{#绑定一个事件#}
<input id="btnSendSms" type="button" value="点击获取验证码" class="btn btn-default"/>
<input type="button" value="提交" id="btnSubmit">
{#这里用的就是urls.py的别名,name=xxx#}
<a href="{% url 'login' %}">账户登录</a>
</form>
function bindSubmitEvent() {
$("#btnSubmit").click(function () {
$.ajax({
url: "/sms/login/",
type: "POST",
data: $("#SmsForm").serialize(),
dataType: "JSON",
success: function (res) {
if(res.status){
{#这里实现跳转#}
location.href = res.data;
}else {
console.log(res.msg)
}
}
})
})
}
完善功能
短信验证码重复使用
redis中删掉就行了
倒计时效果
function bindSendSmsEvent() {
$("#btnSendSms").click(function () {
$.ajax({
url: "/send/sms/",
type: "POST",
data: {
{#固定方法#}
mobile: $("#id_mobile").val(),
role: $("#id_role").val(),
},
dataType: "JSON",
success: function (res) {
if (res.status) {
{#点击之后不允许再点击#}
$("#btnSendSms").prop("disabled", true);
{#这里自定义一个定时器#}
let time = 60
let remind = setInterval(function () {
{#动态效果#}
$("#btnSendSms").val(time + "秒重新发送");
time = time - 1;
if (time < 1) {
$("#btnSendSms").val("点击获取验证码");
{#重新记录时间#}
clearInterval(remind);
{#这时候可以重新点击#}
$("#btnSendSms").prop("disabled", false);
}
}, 1000);
{#出现负数,显然不合理。需要重新赋值#}
} else {
}
}
})
});
}
错误信息展示
//以短信登录为例
function bindSendSmsEvent() {
$("#btnSendSms").click(function () {
$(".error-msg").empty()
$.ajax({
url: "/send/sms/",
type: "POST",
data: {
{#固定方法#}
mobile: $("#id_mobile").val(),
role: $("#id_role").val(),
},
dataType: "JSON",
success: function (res) {
if (res.status) {
{#点击之后不允许再点击#}
$("#btnSendSms").prop("disabled", true);
{#这里自定义一个定时器#}
let time = 60
let remind = setInterval(function () {
{#动态效果#}
$("#btnSendSms").val(time + "秒重新发送");
time = time - 1;
if (time < 1) {
$("#btnSendSms").val("点击获取验证码");
{#重新记录时间#}
clearInterval(remind);
{#这时候可以重新点击#}
$("#btnSendSms").prop("disabled", false);
}
}, 1000);
{#出现负数,显然不合理。需要重新赋值#}
} else {
{#错误信息展示#}
$.each(res.msg, function (k, v) {
$("#id_" + k).next().text(v[0]);
})
}
}
})
});
}
#settings.py
#中文
LANGUAGE_CODE = 'zh-hans'