1.前端代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css">
</head>
<body>
<style>
.box {
width: 480px;
border: 1px solid silver;
margin-left: auto;
margin-right: auto;
margin-top: 100px;
padding: 40px 80px 40px 80px;
box-shadow: 10px 10px 10px rgb(0 0 0 /5%);
}
.sms {
float: right;
}
.text {
text-align: center;
}
.error-message {
color: red;
position: absolute;
}
</style>
<div class="box">
<h1 class="text">短信登录</h1>
<form method="post" id="sms_form" novalidate>
{% csrf_token %}
{% for field in form %}
{% if field.name == "code" %}
<div class="form-group" style="position:relative; margin-bottom: 20px">
<label for="username">{{ field.label }}</label>
<div class="row">
<div class="col-xs-7">
{{ field }}
<span class="error-message">{{ filed.errors.0 }}</span>
</div>
<div class="col-xs-5">
<input type="button" class="btn btn-default" id="sendBtn" value="点击获取验证码">
</div>
</div>
</div>
{% else %}
<div class="form-group" style="position:relative; margin-bottom: 20px">
<label>{{ field.label }}</label>
{{ field }}
<span class="error-message">{{ filed.errors.0 }}</span>
</div>
{% endif %}
{% endfor %}
<button type="button" id="LoginBtn" class="btn btn-primary">登录</button>
<a href="{% url 'login' %}" class="sms">用户登录</a>
</form>
</div>
<script src="/static/js/jquery-3.4.1.js"></script>
<script src="/static/js/csrf.js"></script>
<script>
$(function () {
bindSendSmsEvent()
BindLoginEvent()
})
function BindLoginEvent() {
$("#LoginBtn").click(function () {
$(".error-message").empty();
$.ajax({
url:{% url 'sms_login' %},
type: "POST",
data: $("#sms_form").serialize(),
dataType: "JSON",
success: function (res) {
if (res.status) {
location.href = res.data
} else {
$.each(res.msg, function (k, v) {
$("#id_" + k).next().text(v[0])
})
}
}
})
})
}
function bindSendSmsEvent() {
// 按钮绑定点击事件
$("#sendBtn").click(function () {
// 1.获取手机号, 向后台发送请求【先不写】
// 清楚所有的错误
$(".error-message").empty();
$.ajax({
url:{% url 'sms_send' %},
type: "POST",
data: {
mobile: $("#id_mobile").val(),
role: $("#id_role").val()
},
dataType: "JSON",
success: function (res) {
console.log(res);
if (res.status) {
sendSmsRemind()
} else {
$.each(res.msg, function (k, v) {
$("#id_" + k).next().text(v[0])
})
}
}
})
});
}
function sendSmsRemind() {
var $smsBtn = $("#sendBtn");
// 2.1 禁用
$smsBtn.prop("disabled", true);
// 2.2 改内容
var time = 60;
var remind = setInterval(function () {
$smsBtn.val(time + "秒重新发送");
time = time - 1;
if (time < 1) {
clearInterval(remind);
$smsBtn.val("点击获取验证码");
$smsBtn.prop("disabled", false);
}
}, 1000);
}
</script>
</body>
</html>
2.后端代码
2.2.1视图函数
sms_login函数是短信登录的逻辑。
sms_send函数是获取点击短信验证码,校验我表单中手机号是否填的正确,后调用第三方的sdk(腾讯云或者阿里云)来发送短信,获取了短信在Redis中缓存等待用户输入校验。
python
def sms_login(request):
res = BaseRespone()
if request.method == 'GET':
form = SmsLoginForm()
return render(request, 'sms_login.html', {"form": form})
print(request.POST)
form = SmsLoginForm(request.POST)
if not form.is_valid():
res.msg = form.errors
return JsonResponse(res.dirt)
mobile = form.cleaned_data['mobile']
code = form.cleaned_data['code']
role = form.cleaned_data['role']
# 验证redis缓存的code跟我输入的code一不一样
conn = get_redis_connection("default")
cache_code = conn.get(mobile)
if not cache_code:
res.msg = {"code": ["短信验证码错误"]}
return JsonResponse(res.dirt)
res.status = True
if code != cache_code.decode("utf-8"):
res.msg = {"code": ["短信验证码错误"]}
return JsonResponse(res.dirt)
if role == "1":
user_object = models.Administrator.objects.filter(active=1, mobile=mobile).first()
else:
user_object = models.Customer.objects.filter(active=1, mobile=mobile).first()
if not user_object:
res.msg = {"mobile": ["手机号不存在"]}
return JsonResponse(res.dirt)
mapping = {"1": "ADMIN", "2": "CUSTOMER"}
request.session['user_info'] = {'role': mapping[role], 'name': user_object.username, 'id': user_object.id}
res.status = True
res.data = settings.LOGIN_HOME
return JsonResponse(res.dirt)
def sms_send(request):
res = BaseRespone()
form = MobileForm(data=request.POST)
if not form.is_valid():
res.msg = form.errors
return JsonResponse(res.dirt, json_dumps_params={"ensure_ascii": False})
mobile = form.cleaned_data['mobile']
# 调用腾讯云的SDK要用到手机号
# 假短信验证码
sms_code = random.randint(1000, 9999)
conn = redis.Redis(host='192.168.110.131', port=6379, password='', encoding='utf-8')
conn.set('18888888889', sms_code, ex=60)
value = conn.get('18888888889')
if value:
res.status = True
return JsonResponse(res.dirt, json_dumps_params={"ensure_ascii": False})
2.2.2 froms组件
python
class SmsLoginForm(forms.Form):
role = forms.ChoiceField(
label="角色",
choices=(("2", "客户"), ("1", "管理员")),
widget=forms.Select(attrs={"class": "form-control"})
)
mobile = forms.CharField(
label="手机号",
validators=[RegexValidator(r'^1[358]\d{9}$', '手机格式错误'), ],
widget=forms.TextInput(attrs={"class": 'form-control', "placeholder": "手机号"})
)
code = forms.CharField(
label="短信验证码",
validators=[RegexValidator(r'^[0-9]{4}$', '验证码格式错误'), ],
widget=forms.TextInput(attrs={"class": 'form-control', "placeholder": "短信验证码"})
)
class MobileForm(forms.Form):
role = forms.ChoiceField(
label="角色",
required=True,
choices=(("2", "客户"), ("1", "管理员")),
widget=forms.Select(attrs={"class": "form-control"})
)
mobile = forms.CharField(
label="手机号",
required=True,
validators=[RegexValidator(r'^1[358]\d{9}$', '手机格式错误'), ]
)
def clean_mobile(self):
role = self.cleaned_data['role']
mobile = self.cleaned_data['mobile']
if not role:
return mobile
if role == '1':
exist = models.Administrator.objects.filter(active=1, mobile=mobile).exists()
else:
exist = models.Customer.objects.filter(active=1, mobile=mobile).exists()
if not exist:
raise ValidationError("手机号不存在")
return mobile