用户登录登出功能
一、用户登录功能实现
1.分析
业务处理流程:
- 判断用户输入的账号是否为空
- 判断用户输入的密码是否为空,格式是否正确
- 判断用户输入的账号与密码是否正确
请求方法:POST
url定义:/user/login/
请求参数:url路径参数
参数 | 类型 | 前端是否必须传 | 描述 |
user_account | 字符串 | 是 | 用户输入的账号可以是手机号也可以是用户名 |
password | 字符串 | 是 | 用户输入的密码 |
remember_me | 字符串 | 是 | 用户输入的“是否记住我” |
注:由于是post请求,在向后端发起请求时,需要附带csrf token
2.后端代码实现
.login接受两个参数,第一个是request对象,第二个是user对象。login方法使用SessionMiddleware将userID存入session当中。注意,在用户还未登录的时候,也存在着匿名用户的Session,在其登陆之后,之前在匿名Session中保留的信息,都会保留下来。这两个方法要结合使用,而且必须要先调用authenticate(),因为该方法会User的一个属性上
1、views.py
# 在user目录下的views.py文件中定义如下类:
import json
import re
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseForbidden
from django.views import View
from django_redis import get_redis_connection
from django.contrib.auth import login
from dj_blog.utils.res_code import res_json,Code
from users.models import User
from users.forms import LoginForm
#登录
class LoginView(View):
def get(self,request):
title = '一支穿云箭'
return render(request,'users/login.html',context={'title':title})
# is_authenticated = true false
def post(self,request):
js_str = request.body
if not js_str:
return res_json(errno=Code.PARAMERR,errmsg='参数错误')
dict_data = json.loads(js_str.decode())
# 数据验证 使用form 表单验证
form = LoginForm(data=dict_data,request=request)
if form.is_valid():
# 表单验证成工处理
return res_json(errno=Code.OK)
else:
# 表单验证失败处理
msg_list = []
for i in form.errors.get_json_data().values():
msg_list.append(i[0].get('message'))
msg_str = '/'.join(msg_list)
return res_json(errno=Code.PARAMERR,errmsg=msg_str)
2、路由urls.py
from django.urls import path
from . import views
app_name = 'users'
urlpatterns = [
# path('',views.index),
path('login/',views.LoginView.as_view(),name='login'),
path('register/',views.RegisterView.as_view(),name='register'),
]
3、表单forms.py
# 在users目录下的forms.py文件中定义如下类:
import re
from django import forms
from django.contrib.auth import login
from django.db.models import Q
from .models import User
from . import constants
class LoginForm(forms.Form):
user_account = forms.CharField()
password = forms.CharField(max_length=20,min_length=6,
error_messages={
'min_length':'密码长度大于6',
'max_length':'密码长度小于20',
'required':'密码不能为空'
})
remember = forms.BooleanField(required=False)
def __init__(self,*a,**k):
self.request =k.pop('request')
super().__init__(*a,**k)
def clean_user_account(self):
user_info = self.cleaned_data.get('user_account')
if not user_info:
raise forms.ValidationError('用户名不能为空')
if not re.match(r'^1[3-9]\d{9}$]', user_info) and (len(user_info)<5 or len(user_info)>20 ):
raise forms.ValidationError('输入的用户名格式错误,请重新输入!')
return user_info
def clean(self):
cleaned_data = super().clean()
user_info = cleaned_data.get('user_account')
pass_wd= cleaned_data.get('password')
rmber = cleaned_data['remember']
# 判断是否是用户名你还是手机号 Q
user_qs = User.objects.filter(Q(mobile=user_info) | Q(username=user_info))
if user_qs:
user = user_qs.first()
# 判断密码
if user.check_password(pass_wd):
if rmber:
self.request.session.set_expiry(constants.SESSION_EXPIRY_TIME)
else:
self.request.session.set_expiry(constants.SESSION_TIME)
login(self.request,user)
else:
raise forms.ValidationError('用户名或密码错误,请重新输入')
else:
raise forms.ValidationError('用户名不存在,请重新输入!')
return cleaned_data
4、constants.py
# 在users目录下的constants.py文件中定义如下常量:
# 用户session信息过期时间,单位秒,这是设置为30天
SESSION_EXPIRY_TIME = 30 * 24 * 60 * 60
#不选记住我就只有7天的
SESSION_TIME = 7 * 24 * 60 * 60
3、前端:
1、login.js
$(function () {
let $login = $('.form-contain');
$login.submit(function (e) {
e.preventDefault();
let sUsername = $('input[name=telephone]').val();
if(sUsername === ''){
message.showError('用户名不能为空!');
return
}
if (!(/^[\u4e00-\u9fa5\w]{5,20}$/.test(sUsername))){
message.showError('请输入5-20位字符的用户名');
return
}
// 密码验证
let sPassword = $('input[name=password]').val();
if(!sPassword){
message.showError('密码不能为空!');
return
}
// 验证用户名 6
if(sPassword.length<6 || sPassword.length>20) {
message.showError('密码长度需要在6-20之间');
return
}
let status = $("input[type='checkbox']").is(':checked');
// g构造参数
let sData ={
'user_account' : sUsername,
'password':sPassword,
'remember':status
};
$.ajax({
url:'/user/login/',
type:'POST',
// headers: {
// // 根据后端开启的CSRFProtect保护,cookie字段名固定为X-CSRFToken
// "X-CSRFToken": getCookie("csrftoken")
// },
data:JSON.stringify(sData),
contentType:'application/json; charset=utf-8',
dataType:'json',
})
.done(function (res) {
if(res.errno==='0'){
message.showSuccess('贵宾,恭喜您登录成功!');
setTimeout(function () {
window.location.href = '/';
},1500)
}else {
message.showError(res.errmsg)
}
})
.fail(function () {
message.showError('服务器超时,请重试!')
})
})
});
写完login的js后要记得在login页面中引入js
2、base页面下的common.js
$(()=>{
let $navLi = $('#header .nav .menu li')
$navLi.click(function(){
$(this).addClass('active').siblings('li').removeClass('active')
});
// get cookie using jQuery
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
let cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
});
记得要在base页面下引入common.js
3、html
修改base页面登录逻辑
{% if user.is_authenticated %}
<div class="author">
<i class="PyWhich py-user"></i>
<span>{{user.username }}</span>
<ul class="author-menu">
<li><a href="javascript:void(0);">后台管理</a></li>
<li><a href="javascript:void(0);">退出登录</a></li>
</ul>
</div>
{% else %}
<div>
<i class="PyWhich py-user"></i>
<span>
<a href="{% url 'users:login' %}" class="login">登录</a> / <a href="{% url 'users:register' %}"
class="reg">注册</a>
</span>
</div>
{% endif %}
如果登录后,会显示用户名
二、用户登出功能实现
1.分析
请求方法:GET
url定义:/user/logout/
实现:调用Django自带的logout(request)函数即可
2.后端代码实现
1、views.py
# 在users目录下的views.py文件中定义如下类:
class LogoutView(View):
"""
"""
def get(self, request):
logout(request)
return redirect(reverse("users:login"))
2、urls.py
# 在users目录下的urls.py文件中定义如下路由:
from django.urls import path
from . import views
app_name = 'users'
urlpatterns = [
path('logout/', views.LogoutView.as_view(), name='logout'),
]
3、前端代码
{% if user.is_authenticated %}
<div class="author">
<i class="PyWhich py-user"></i>
<span>{{user.username }}</span>
<ul class="author-menu">
{% if user.is_staff %}
<li><a href="javascript:void(0);">后台管理</a></li>
{% endif %}
<li><a href="{% url 'users:logout' %}">退出登录</a></li>
</ul>
</div>
{% else %}
<div>
<i class="PyWhich py-user"></i>
<span>
<a href="{% url 'users:login' %}" class="login">登录</a> / <a href="{% url 'users:register' %}"
class="reg">注册</a>
</span>
</div>
{% endif %}
总结:
1、用户登录这次使用了表单进行校验,如何把request对象传入表单中,如何从form.errors取出错误进行拼接。
2、通过上下文处理器模板中已经传入user对象,可以通过user来判断时是否登录,是否可以用户后台管理功能。
3、json数据通过request.body进行取值。
4、这次通过js获得csrf并在请求头中加入了csrf,不用每次ajax都要写这个csrf请求头。(结合前面的中间件生成的csrf)