1、使用django自带功能实现登录/退出登录
使用django自带登录功能,前提生成用户(用户注册)使用的是django自带的user,或称models中用户表继承于django自带的user
1.1、登录
views.py:
from django.contrib.auth import authenticate,login,logout
def acc_login(request):
"""登录页面"""
error_msg = ''
if request.method == "POST":
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username = username, password = password)
if user:
login(request,user)
return redirect(request.GET.get('next','/index/')) # 如果有next则跳转到next指定的页面,如果没有则跳转到index页面
else:
error_msg = "Wrong username or password!"
return render(request,"login.html",{'error_msg':error_msg})
1.2、退出登录
views.py:
def acc_logout(request):
logout(request)
return redirect("/login/")
urls.py:
from django.urls import path
from NBCRM import views
urlpatterns = [
path('login/',views.acc_login),
path('logout/',views.acc_logout,name="logout"),
]
settings.py:
当某些页面设置了登录才能访问时,用户访问该页面会失败,此时可以在settings中进行下面的设置,指定用户在未登录前访问需登录才能访问的页面时,跳转到指定(登录)页面,执行某些操作,成功后才能访问目标页面。
LOGIN_URL = '/login/'
登录界面:

2.自定义登录方式
1)login相关代码:
def login(request):
if request.method == "POST":
username = request.POST.get("username")
password = request.POST.get("password")
user = models.User.objects.filter(username=username, password=password) # [User Obj, ]
if user:
# 登陆成功
request.session["is_login"] = "1"
# request.session["username"] = username
request.session["user_id"] = user[0].id
# 1. 生成特殊的字符串
# 2. 特殊字符串当成key,在数据库的session表中对应一个session value
# 3. 在响应中向浏览器写了一个Cookie Cookie的值就是 特殊的字符串
return redirect("/index/")
return render(request, "login.html")
2)check_login:
装饰器,用于检查用户是否登录
from functools import wraps
def check_login(f):
@wraps(f)
def inner(request, *args, **kwargs):
if request.session.get("is_login") == "1":
return f(request, *args, **kwargs)
else:
return redirect("/login/")
return inner
3)index:
首页,访问前判断用户是否已登录,登录则成功访问,未登录则跳转登录页面
@check_login
def index(request):
user_id = request.session.get("user_id")
# 根据id去数据库中查找用户
user_obj = models.User.objects.filter(id=user_id)
if user_obj:
return render(request, "index.html", {"user": user_obj[0]})
else:
return render(request, "index.html", {"user": "匿名用户"})
2.1、session简单介绍
1)session作用
- 生成随机字符串
- 写到用户浏览器的cookie中
- 保存到session中
- 在随机字符串对应的字典中设置相关内容
2)session默认过期时间为2周,如果自己设置过期时间,自定义设置过期时间优先级高于默认时间
request.session.set_expiry(value) # 设置session过期时间
默认的过期时间是两周,如果自己设置了过期时间,这样自己设定的优先级就会高于默认的
如果value是个整数,session会在些秒数后失效。
如果value是个datatime或timedelta,session就会在这个时间后失效。
如果value是0,用户关闭浏览器session就会失效。
如果value是None,session会依赖全局session失效策略。
3)session的相关配置
# settings.py SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
4)Django中对应session的存储方式
Django中支持session,其中内部提供了5种类型的session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
1、如果是数据库,需要在settings.py中配置如下: SESSION_ENGINE = 'django.contrib.sessions.backends.db' (引擎(默认)) 2、如果是缓存session,需要在settings.py中配置如下: SESSION_ENGINE = 'django.contrib.sessions.backends.cache'(引擎) SESSION_CACHE_ALIAS= 'default' 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 3、 如果是文件session, 需要在settings.py中配置如下: SESSION_ENGINE = 'django.contrib.sessions.backends.file' (引擎) SESSION_FILE_PATH=None 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 4、如果是缓存+数据库session,需要在settings.py中配置如下: SESSION_ENGINE='django.contrib.sessions.backends.cached_db' (引擎)
3、利用django自带登录功能,实现自定义用户名或邮箱登录,及用户注册(邮箱激活)、忘记密码等功能
3.1 实现用户注册功能

3.1.1 验证码实现 --第三方库:Captcha
1)安装captcha
pip install django-simple-captcha
2)在我们项目的setting.py中的INSTALLED_APPS注册captcha。需要注意的是,在INSTALLED_APPS中注册的,都需要经过makemigrations、migrate生成对应的表数据
INSTALLED_APPS = [
……
'captcha',
……
]
3)配置urls.py
from django.urls import path,include,re_path
urlpatterns = [
# 验证码
re_path(r'^captcha', include('captcha.urls')),
]
通过上述几步,我们就能在项目中正常使用captcha了。用法很简单:
1)后台用到验证码的地方:
from captcha.fields import CaptchaField
class RegisterForm(forms.Form):
"""注册表单验证"""
captcha = CaptchaField(error_messages={'invalid':'验证码错误'})
2)前端页面展示 --关于验证码:
<label>验 证 码</label>
{{ register_form.captcha }}
使用captcha实现验证码的好处:不需要我们判断前端验证码输入与原验证码是否一致(内部已实现了判断),只需要按上述几个步骤操作,即可实现注册/登录页面的验证码验证功能
3.1.2 实现注册功能
结合第三方库captcha,实现附有验证码的注册功能
1)注册功能 --form表单验证

from django import forms
from django.core.exceptions import ValidationError
from captcha.fields import CaptchaField
class RegisterForm(forms.Form):
"""注册表单验证"""
email = forms.EmailField(required=True)
password = forms.CharField(required=True,min_length=6)
# 验证码 内部已经包含判断验证码是否正确,不需要进行下述的验证码验证代码编写
captcha = CaptchaField(error_messages={'invalid':'验证码错误'})
# 验证码验证,使用第三方库captcha时不需要再判断,属于画蛇添足
# def clean_captcha(self):
# print("captcha:",self.request.session.get('code'))
# print("valid_code:",self.cleaned_data.get('code'))
# if self.request.session.get('code').upper() == self.cleaned_data.get('code').upper():
# return self.cleaned_data['code']
# else:
# raise ValidationError('验证码不正确')
# 自定义方法(局部钩子),密码必须包含字母和数字
def clean_password(self):
if self.cleaned_data.get('password').isdigit() or self.cleaned_data.get('password').isalpha():
raise ValidationError('密码必须包含数字和字母')
else:
return self.cleaned_data['password']
# 自定义方法(全局钩子, 检验两个字段),可用于检验两次密码是否一致;
# def clean(self):
# if self.cleaned_data.get('password') != self.cleaned_data.get('password2'):
# raise ValidationError('密码不一致')
# else:
# return self.cleaned_data
2)views.py:
def register(request):
"""用户注册 """
if request.method == "POST":
register_form = forms.RegisterForm(request.POST)
if register_form.is_valid():
user_name = request.POST.get('email',None)
if UserProfile.objects.filter(email=user_name):
# 用户已经存在,不需要再注册
return render(request,'register.html',{'msg':'用户已经存在','register_form':register_form})
pass_word = request.POST.get('password', None)
# 将密码加密后再保存
pass_word = make_password(pass_word)
UserProfile.objects.create(
username=user_name,
email=user_name,
is_active=False,
password=pass_word
)
email_send_status = email_send.send_register_email(user_name, 'register') # 发送邮件,用于用户激活账号
if email_send_status: # 状态为1,表示邮件发送成功
email_send_success = True # 用于前端判断发送邮件的类型
return render(request,'send_email_success.html',locals())
else:
# status_form = True #用于前端注册时判断是否填充客户输入原数据
return render(request, 'register.html', {'msg':'用户名或密码格式错误','register_form':register_form})
else:
register_form = forms.RegisterForm()
return render(request,'register.html',locals())
3)urls.py配置:
from django.urls import path
from web_online import views
urlpatterns = [
path('register/',views.register, name='register'),
# 验证码url
re_path(r'^captcha',include('captcha.urls')),
]
4)HTML前端代码(仅供参考):

<!DOCTYPE html>
{% load staticfiles %}
<html>
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" >
<title>慕学在线注册</title>
<link rel="stylesheet" type="text/css" href="{% static 'css/reset.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/login.css' %}">
</head>
<body>
<div class="loginbox dialogbox">
<h1>账号登录</h1>
<div class="close jsCloseDialog"><img src="{% static 'images/dig_close.png' %}"/></div>
<div class="cont">
<form id="jsLoginForm" autocomplete="off">
<div class="box">
<span class="word3">用户名</span>
<input type="text" name="account_l" id="account_l" placeholder="手机号/邮箱" />
</div>
<div class="box">
<span class="word2">密 码</span>
<input type="password" name="password_l" id="password_l" placeholder="请输入您的密码"/>
</div>
<div class="error btns login-form-tips" id="jsLoginTips"></div> <!--登录错误提示-->
<div class="btns">
<span><input type="checkbox" id="jsAutoLogin" /> 自动登录</span>
<span class="forget btn fr">忘记密码</span>
</div>
<div class="button">
<input type="button" value="登录" id="jsLoginBtn" />
</div>
<div class="btns">
<span class="fr">没有账号?<span class="regist btn">立即注册</span></span>
</div>
</form>
</div>
</div>
<div class="dialog" id="jsDialog">
<!--提示弹出框-->
<div class="successbox dialogbox" id="jsSuccessTips">
<h1>成功提交</h1>
<div class="close jsCloseDialog"><img src="{% static 'images/dig_close.png' %}"/></div>
<div class="cont">
<h2>您的需求提交成功!</h2>
<p></p>
</div>
</div>
<div class="noactivebox dialogbox" id="jsUnactiveForm" >
<h1>邮件验证提示</h1>
<div class="close jsCloseDialog"><img src="{% static 'images/dig_close.png' %}"/></div>
<div class="center">
<img src="{% static 'images/send.png' %}"/>
<p>我们已经向您的邮箱<span class="green" id="jsEmailToActive">12@13.com</span>发送了邮件,<br/>为保证您的账号安全,请及时验证邮箱</p>
<p class="a"><a class="btn" id="jsGoToEmail" target="_blank" href="http://mail.qq.com">去邮箱验证</a></p>
<p class="zy_success upmove"></p>
<p style="display: none;" class="sendE2">没收到,您可以查看您的垃圾邮件和被过滤邮件,也可以再次发送验证邮件(<span class="c5c">60s</span>)</p>
<p class="sendE">没收到,您可以查看您的垃圾邮件和被过滤邮件,<br/>也可以<span class="c5c green" id="jsSenEmailAgin" style="cursor: pointer;">再次发送验证邮件</span></p>
</div>
</div>
</div>
<div class="bg" id="dialogBg"></div>
<header>
<div class="c-box fff-box">
<div class="wp header-box">
<p class="fl hd-tips">慕学在线,在线学习平台!</p>
<ul class="fr hd-bar">
<li>服务电话:<span>4001008031</span></li>
<li><a href="{% url 'login' %}">[登录]</a></li>
<li class="active"><a href="{% url 'register' %}">[注册]</a></li>
</ul>
</div>
</div>
</header>
<section>
<div class="c-box bg-box">
<div class="login-box clearfix">
<div class="hd-login clearfix">
<a class="index-logo" href="/index/"></a>
<h1>用户注册</h1>
<a class="index-font" href="/index/">回到首页</a>
</div>
<div class="fl slide">
<div class="imgslide">
<ul class="imgs">
<li><a href=""><img width="483" height="472" src="{% static 'images/57a801860001c34b12000460.jpg' %}" /></a></li>
<li><a href=""><img width="483" height="472" src="{% static 'images/57a801860001c34b12000460.jpg' %}" /></a></li>
<li><a href=""><img width="483" height="472" src="{% static 'images/57a801860001c34b12000460.jpg' %}" /></a></li>
</ul>
</div>
<div class="unslider-arrow prev"></div>
<div class="unslider-arrow next"></div>
</div>
<div class="fl form-box">
<div class="tab">
<!--<h2 class="active">手机注册</h2>-->
<h2>邮箱注册</h2>
</div>
<div class="tab-form">
<form id="email_register_form" method="post" action="{% url 'register' %}" autocomplete="off">
{% csrf_token %}
<div class="form-group marb20 {% if register_form.errors.email.0 %} errorput {% endif %}">
<label>邮 箱</label>
<input type="text" id="id_email" name="email" {% if register_form.email.value %} value="{{ register_form.email.value }}" {% endif %} placeholder="请输入您的邮箱地址" />
</div>
<div class="error btns" id="jsEmailTips">{{ register_form.errors.email.0 }}</div>
<div class="form-group marb8 {% if register_form.errors.password.0 %} errorput {% endif %}">
<label>密 码</label>
<input type="password" id="id_password" name="password" {% if register_form.password.value %} value="{{ register_form.password.value }}" {% endif %} placeholder="请输入6-20位非中文字符密码" />
</div>
<div class="error btns" id="jsEmailTips">{{ register_form.errors.password.0 }}</div>
<div class="form-group marb8 captcha1 {% if register_form.errors.captchal.0 %} errorput {% endif %}">
{# <label>验 证 码</label>#}
{# {{ register_form.captcha }}#}
<input name='code' type="text" placeholder="验证码" />
{{ register_form.captcha }}
</div>
<div class="error btns" id="jsEmailTips">{{ register_form.errors.captcha.0 }}</div>
<div class="error btns" id="jsEmailTips">
{{ msg }}
</div>
<div class="auto-box marb8">
</div>
<input class="btn btn-green" id="jsEmailRegBtn" type="submit" value="注册" />
</form>
</div>
<p class="form-p">已有账号?<a href="{% url 'login' %}">[立即登录]</a></p>
</div>
</div>
</div>
</section>
<input id="isLogin" type="hidden" value="False"/>
<script src="{% static 'js/jquery.min.js' %}" type="text/javascript"></script>
<script src="{% static 'js/unslider.js' %}" type="text/javascript"></script>
<script src="{% static 'js/validateDialog.js' %}" type="text/javascript"></script>
<script src="{% static 'js/login.js' %}" type="text/javascript"></script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if email_send_success %}
<p>【注册账号激活】邮件已发送,请移步邮箱中查收!</p>
{% else %}
<p>【密码重置】邮件已发送,请移步邮箱中查收!</p>
{% endif %}
</body>
</html>
3.1.3 注册账号激活
在上述注册功能实现过程中涉及到邮件激活账号:email_send.send_register_email(user_name,'register') , 下面实现注册账号激活功能
注意:用户账号激活时,本质是通过is_active=True来实现的,如果将is_staff也设置为True,则表示该用户可以登录我们项目的django后台。切记,只有is_active、is_staff同时为True,才能登录django后台。
1)email_send.py:用于给用户发送邮件,实现下述功能
- 账号激活
- 忘记密码,重置密码
- 修改密码

import random
from users.models import EmailVerifyRecord
from django.core.mail import send_mail
from web_online import settings
def random_str(random_length=16):
"""默认生成16位随机字符串"""
str = ''
# 生成字符串的可选字符串
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 1
# ran_dom = random.Random()
for i in range(random_length):
str += chars[random.randint(0, length)]
return str
# 发送邮件
def send_register_email(email, send_type="register"):
"""
发送邮件
发送前将当前注册用户保存到数据库,方便后台激活账号时能在数据库中找到对应的注册用户
"""
if send_type == 'update_email': # 修改密码操作
code = random_str(4)
else:
code = random_str(16)
# 保存到数据库
EmailVerifyRecord.objects.create(
code=code,
email=email,
send_type=send_type
)
# 定义邮箱内容:
if send_type == "register": # 注册激活账号
subject = "Mx在线教育注册激活链接" # 标题
email_body = "请复制打开下面的链接激活你的账号:http://127.0.0.1:8000/active/{0}".format(code) # 文本邮件体
sender = settings.DEFAULT_FROM_EMAIL # 发件人
receiver = [email] # 接收人
email_send_status = send_mail(subject, email_body, sender, receiver)
return email_send_status
# if email_send_status:
# pass
elif send_type == 'forget': # 忘记密码 重置密码
subject = "Mx在线教育重置密码链接" # 标题
email_body = "请复制打开下面的链接重置密码:http://127.0.0.1:8000/reset/{0}".format(code) # 文本邮件体
sender = settings.DEFAULT_FROM_EMAIL # 发件人
receiver = [email] # 接收人
email_send_status = send_mail(subject, email_body, sender, receiver)
return email_send_status
elif send_type == "update_email": # 修改密码验证码
subject = "Mx在线教育邮箱修改验证码"
email_body = "你的邮箱验证码为{0}".format(code)
sender = settings.DEFAULT_FROM_EMAIL
receiver = [email]
# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者list
send_status = send_mail(subject, email_body, sender, receiver)
# 如果发送成功
if send_status:
pass
2)使用django自带的邮件发送功能,需要在settings.py中配置发送邮件的基本数据:
# 邮箱配置 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = "smtp.163.com" #以163邮箱为例,SMTP服务器(邮箱需要开通SMTP服务) EMAIL_HOST_PASSWORD = '******' #SMTP服务授权码 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER = "13*******@163.com" #我的163邮箱帐号 EMAIL_PORT = 25 #163邮箱SMTP服务端口 EMAIL_USE_TLS = True # 163、qq邮箱此值为True,aliyun此值为False,163可以忽略此值 # EMAIL_SUBJECT_PREFIX = '[yshblog.com]' #邮件标题前缀,默认是'[django]'
3)views.py:
注册账号激活功能
def user_active(request, accode):
"""注册用户账号激活"""
if request.method == "GET":
ac_records = EmailVerifyRecord.objects.filter(code=accode)
if ac_records: # 有当前注册用户
five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
ac_record = ac_records[0]
# print("five_mintes_ago:",five_mintes_ago)
# print("send_time:",ac_record.send_time)
if five_mintes_ago > ac_record.send_time: # 发送时间超过5分钟,返回链接失效页面
return render(request, 'active_fail.html')
ac_email = ac_record.email
ac_user = UserProfile.objects.get(email=ac_email) # 当前注册用户
ac_user.is_active = True
ac_user.save()
return render(request,'active_success.html',locals())
else:
return render(request,'active_fail.html')
4)urls.py配置:
from django.urls import path
from web_online import views
urlpatterns = [
re_path(r'^active/(\w+)/', views.user_active, name='user_active'), # 邮箱激活账号
]
5)注册成功返回页面、注册用户失败或链接失效返回页面:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>mx教育邮箱注册成功</title>
</head>
<body>
<h3>恭喜!您已成功激活账号:{{ ac_email }}</h3>
<p style="color: red">点击跳转:<a href="/login/">登录</a></p>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p style="color: red;">链接已经失效</p>
</body>
</html>
至此,用户注册及激活功能即完成。
3.2 实现自定义用户名或邮箱登录功能
3.2.1 userProfile继承于 AbstractUser
需要注意的是:AbstractUser中定义的email字段不具有唯一性,容易造成多个用户使用同一邮箱,当使用邮箱登录时会造成冲突。因此,需要重写email字段,使其与username字段一样,具备 '唯一'性
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
"""用户"""
gender_choices = (
('male','男'),
('female','女')
)
nick_name = models.CharField('昵称',max_length=32 ,default='')
birthday = models.DateField('生日',null=True,blank=True)
gender = models.CharField('性别',max_length=8,choices=gender_choices,default='female')
adress = models.CharField('地址',max_length=100,default='')
mobile = models.CharField('手机号',max_length=11,null=True,blank=True)
image = models.ImageField(upload_to='image/%Y/%m',default='image/default.png',max_length=100)
email = models.EmailField('邮箱', blank=True,unique=True) # 重写email字段,加上'唯一'标识
class Meta:
verbose_name = '用户信息'
verbose_name_plural = verbose_name
def __str__(self):
return self.username
此时 userProfile表所存字段:

3.2.2 在views中编写登录功能之前,我们需要先写一个form表单,用于用户登录时,对用户名及密码格式进行验证:
forms.py/LoginForm
from django import forms
class LoginForm(forms.Form):
"""登录表单验证"""
username = forms.CharField(required=True)
password = forms.CharField(required=True, min_length=6)
3.2.3 接着在views界面中编写我们的登录代码:
from django.shortcuts import render,redirect
from django.contrib.auth import authenticate ,login ,logout
from users.models import UserProfile
from web_online import forms
def mx_login(request):
"""登录"""
login_form = forms.LoginForm()
if request.method == "POST":
login_form = forms.LoginForm(request.POST)
if login_form.is_valid():
user_name = request.POST.get('username',None)
pass_word = request.POST.get('password',None)
user = authenticate(username=user_name, password=pass_word)
if user:
if user.is_active:
# 只有注册激活才能登陆
login(request,user)
return redirect('/index/')
else:
return render(request,'login.html',{'msg': '用户未激活', 'login_form': login_form})
else:
return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form})
else:
return render(request, 'login.html', {'msg': '用户名或密码格式错误,请重新输入!', 'login_form': login_form})
return render(request,"login.html")
简单解析:
1. is_active:在注册中,我们对成功激活账号的用户设置is_active为True,在登录操作中就可以通过is_active的状态来判断用户是否是注册成功的用户,来决定是否给用户登录权限
2. authenticate()方法的验证:我们使用的是django自带的authenticate验证, 默认只对用户名、密码进行验证,我们要使用用户名或邮箱进行验证,需要自定义authenticate方法,实现代码如下:
class CustomBackend(ModelBackend):
"""
用于mx_login用户登录验证
需在settings中配置好authenticate验证方式(即在此进行authenticate的相关验证)
"""
def authenticate(self, request, username=None, password=None, **kwargs):
# 重写authenticate方法
try:
user = UserProfile.objects.get(Q(username=username)|Q(email=username))
if user.check_password(password):
"""
1.在注册时,我们对密码使用了加密处理(django下的make_password),因此在登录验证密码时需要先将明文密码加密
后才能跟数据库中已加密后的密码进行比较
2.check_password()是AbstractUser类中的方法,UserProfile继承于AbstractUser,check_password会加密明文密
码后,与数据库密码做对比,再进行判断两密码是否一致
3.验证如果为True,表示密码一致;为False,表示密码不一致
"""
return user
else:
return None
except Exception as e:
return None
以上基本实现了自定义登录功能验证,但要验证流程中的authenticate验证走我们自定义的authenticate验证,需要在settings.py中配置AUTHENTICATION_BACKENDS ,如不配置,默认会走django自带的authenticate验证:
AUTHENTICATION_BACKENDS = ( # 登录认证设置
'web_online.views.CustomBackend', #CustomBackend所在路径
)
3.2.4 url配置:
from django.urls import path
from web_online import views
urlpatterns = [
path('login/',views.mx_login, name='login'),
]
3.2.5 html配置:
注意:HTML前端代码未附带css、js代码,仅供参考

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" >
<title>慕学在线网登录</title>
<link rel="stylesheet" type="text/css" href="/static/css/reset.css">
<link rel="stylesheet" type="text/css" href="/static/css/login.css">
</head>
<body>
<div class="dialog" id="jsDialog">
<!--提示弹出框,用于找回密码操作时的提示-->
<div class="successbox dialogbox" id="jsSuccessTips">
<h1>成功提交</h1>
<div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div>
<div class="cont">
<h2>您的需求提交成功!</h2>
<p></p>
</div>
</div>
<div class="noactivebox dialogbox" id="jsUnactiveForm" >
<h1>邮件验证提示</h1>
<div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div>
<div class="center">
<img src="/static/images/send.png"/>
<p>我们已经向您的邮箱<span class="green" id="jsEmailToActive">12@13.com</span>发送了邮件,<br/>为保证您的账号安全,请及时验证邮箱</p>
<p class="a"><a class="btn" id="jsGoToEmail" target="_blank" href="http://mail.qq.com">去邮箱验证</a></p>
<p class="zy_success upmove"></p>
<p style="display: none;" class="sendE2">没收到,您可以查看您的垃圾邮件和被过滤邮件,也可以再次发送验证邮件(<span class="c5c">60s</span>)</p>
<p class="sendE">没收到,您可以查看您的垃圾邮件和被过滤邮件,<br/>也可以<span class="c5c green" id="jsSenEmailAgin" style="cursor: pointer;">再次发送验证邮件</span></p>
</div>
</div>
</div>
<div class="bg" id="dialogBg"></div>
<header>
<div class="c-box fff-box">
<div class="wp header-box">
<p class="fl hd-tips">慕学在线网,在线学习平台!</p>
<ul class="fr hd-bar">
<li>服务电话:<span>33333333</span></li>
<li class="active"><a href="{% url 'login' %}">[登录]</a></li>
<li><a href="{% url 'register' %}">[注册]</a></li>
</ul>
</div>
</div>
</header>
<section>
<div class="c-box bg-box">
<div class="login-box clearfix">
<div class="hd-login clearfix">
<a class="index-logo" href="/index/"></a>
<h1>用户登录</h1>
<a class="index-font" href="/index/">回到首页</a>
</div>
<div class="fl slide">
<div class="imgslide">
<ul class="imgs">
<li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg"/></a></li>
<li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg"/></a></li>
<li><a href=""><img width="483" height="472" src="/static/images/mysql.jpg"/></a></li>
</ul>
</div>
<div class="unslider-arrow prev"></div>
<div class="unslider-arrow next"></div>
</div>
<div class="fl form-box">
<h2>帐号登录</h2>
<form action="/login/" method="post" autocomplete="off">
{% csrf_token %}
<div class="form-group marb20 {% if login_form.errors.username.0 %} errorput {% endif %}">
<label>用 户 名</label>
<input name="username" id="account_l" type="text" placeholder="手机号/邮箱" {% if login_form.username.value %}value="{{ login_form.username.value }}"{% endif %}/>
</div>
<div class="error btns login-form-tips" id="jsLoginTips">{{ login_form.errors.username.0 }}</div>
<div class="form-group marb8 {% if login_form.errors.password.0 %} errorput {% endif %}">
<label>密 码</label>
<input name="password" id="password_l" type="password" {% if login_form.password.value %}value="{{ login_form.password.value }}"{% endif %} placeholder="请输入您的密码"/>
</div>
<div class="error btns login-form-tips" id="jsLoginTips">{{ login_form.errors.password.0 }}</div>
<div class="error btns login-form-tips" id="jsLoginTips">{{ msg }}</div>
<div class="auto-box marb38">
<a class="fr" href="#">忘记密码?</a>
{# <a class="fr" href="{% url 'forgetpwd' %}">忘记密码?</a>#}
</div>
<input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > "/>
<input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy'/>
{% csrf_token %}
</form>
<p class="form-p">没有慕学在线网帐号?<a href="{% url 'register' %}">[立即注册]</a></p>
</div>
</div>
</div>
</section>
<script src="/static/js/jquery.min.js" type="text/javascript"></script>
<script src="/static/js/unslider.js" type="text/javascript"></script>
<script src="/static/js/login.js" type="text/javascript"></script>
</body>
</html>
3.3 退出登录功能
3.3.1 退出登录
实现退出登录功能很简单,在views.py中编写退出登录代码:
from django.contrib.auth import logout
def mx_logout(request):
"""退出登录"""
logout(request)
return redirect('/login/') # 返回登录页面
3.3.2 url配置:
from django.urls import path
from web_online import views
urlpatterns = [
path('logout/',views.mx_logout, name='logout'),
]
注:退出登录功能,没有HTML前端代码
3.4 实现忘记密码、找回密码功能
点击 [忘记密码],进入找回密码,根据注册邮箱,找回密码

3.4.1 根据注册用户邮箱,发送验证邮件
1)views.py:
def forget_pwd(request):
"""忘记密码,通过邮箱找回密码"""
message = {}
if request.method == "POST":
forget_pwd_form = forms.ForgetPwdForm(request.POST)
if forget_pwd_form.is_valid():
email = request.POST.get('email',None)
user_objs = UserProfile.objects.filter(email=email)
if user_objs: # 判断邮箱是否存在
send_status = email_send.send_register_email(email,'forget')
if send_status: # 邮件发送成功
email_send_success = False # 用于前端判断发送邮件的类型
return render(request,'send_email_success.html',locals())
else:
message['msg'] = '该邮箱不存在'
message['status'] = True
return render(request, 'forgetpwd.html', {'message': message, 'forget_pwd_form': forget_pwd_form})
else: # form表单验证不通过
message['msg'] = '邮箱或验证码错误'
message['status'] = True
return render(request,'forgetpwd.html',{'message':message,'forget_pwd_form' : forget_pwd_form})
else:
forget_pwd_form = forms.ForgetPwdForm()
return render(request,'forgetpwd.html',{'forget_pwd_form' : forget_pwd_form})
2)forms.py 表单验证:
class ForgetPwdForm(forms.Form):
"""忘记密码"""
email = forms.EmailField(required=True)
captcha = CaptchaField(error_messages={'invalid': '验证码错误'})
3)urls.py:
from django.urls import path
from web_online import views
urlpatterns = [
path('forgetpwd/', views.forget_pwd, name='forgetpwd'), # 忘记密码
]
4)HTML前端代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<title>慕学网首页</title>
<link rel="stylesheet" type="text/css" href="/static/css/reset.css">
<link rel="stylesheet" type="text/css" href="/static/css/login.css">
</head>
<body>
<!--提示弹出框-->
<div class="successbox dialogbox" id="jsSuccessTips">
<h1>成功提交</h1>
<div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div>
<div class="cont">
<h2>您的需求提交成功!</h2>
<p></p>
</div>
</div>
<div class="resetpassbox dialogbox" id="jsSetNewPwd">
<h1>重新设置密码</h1>
<div class="close jsCloseDialog"><img src="/static/images/dig_close.png"/></div>
<p class="green">请输入新密码</p>
<form id="jsSetNewPwdForm">
<div class="box">
<span class="word2">密 码</span>
<input type="password" name="password" id="jsResetPwd" placeholder="请输入新密码"/>
</div>
<div class="box">
<span class="word2">确 认 密 码</span>
<input type="password" name="password2" id="jsResetPwd2" placeholder="请再次输入新密码"/>
</div>
<div class="box">
<span class="word2">验 证 码</span>
<input type="text" name="code" id="jsResetCode" placeholder="请输入手机验证码"/>
</div>
<div class="error btns" id="jsSetNewPwdTips"></div>
<div class="button">
<input type="hidden" name="mobile" id="jsInpResetMobil"/>
<input id="jsSetNewPwdBtn" type="button" value="提交"/>
</div>
</form>
</div>
<div class="bg" id="dialogBg"></div>
<header>
<div class="c-box fff-box">
<div class="wp header-box">
<p class="fl hd-tips">慕学网,在线学习平台!</p>
<ul class="fr hd-bar">
<li>服务电话:<span>33333333</span></li>
<li><a href="{% url 'login' %}">[登录]</a></li>
<li class="active"><a href="/forgetpwd/">[忘记密码]</a></li>
</ul>
</div>
</div>
</header>
<section>
<div class="c-box bg-box">
<div class="login-box clearfix">
<div class="hd-login clearfix">
<a class="index-logo" href="{% url 'index' %}"></a>
<h1>忘记密码</h1>
<a class="index-font" href="{% url 'index' %}">回到首页</a>
</div>
<div class="fl slide">
<div class="imgslide">
<ul class="imgs">
<li><a href=""><img width="483" height="472" src="/static/images/57a801860001c34b12000460.jpg"/></a>
</li>
<li><a href=""><img width="483" height="472" src="/static/images/57a801860001c34b12000460.jpg"/></a>
</li>
<li><a href=""><img width="483" height="472" src="/static/images/57a801860001c34b12000460.jpg"/></a>
</li>
</ul>
</div>
<div class="unslider-arrow prev"></div>
<div class="unslider-arrow next"></div>
</div>
<div class="fl form-box">
<h2>忘记密码</h2>
<form id="jsFindPwdForm" method="post" autocomplete="off">
{% csrf_token %}
<div class="form-group marb20 {% if forget_pwd_form.errors.email.0 %} errorput {% endif %}">
<label>帐 号</label>
<input type="text" id="account" name="email" {% if message.status %} value="{{ forget_pwd_form.email.value }}" {% endif %} placeholder="请输入邮箱号"/>
</div>
<div class="form-group captcha1 marb38 {% if forget_pwd_form.errors.captchal.0 %} errorput {% endif %}">
<label>验 证 码</label>
{{ forget_pwd_form.captcha }}
</div>
<div class="error btns" id="jsForgetTips">{{ message.msg }}</div>
<input type="hidden" name="sms_type" value="1">
<input class="btn btn-green" id="jsFindPwdBtn" type="submit" value="提交"/>
<p class="form-p" style="bottom:40px;">您还可以<a href="{% url 'login' %}"> [直接登录]</a></p>
</form>
</div>
</div>
</div>
</section>
<input id="isLogin" type="hidden" value="False"/>
<script src="/static/js/jquery.min.js" type="text/javascript"></script>
<script src="/static/js/unslider.js" type="text/javascript"></script>
<script src="/static/js/validateDialog.js" type="text/javascript"></script>
<script src="/static/js/login.js" type="text/javascript"></script>
</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if email_send_success %}
<p>【注册账号激活】邮件已发送,请移步邮箱:{{ user_name }} 中查收!</p>
{% else %}
<p>【密码重置】邮件已发送,请移步邮箱:{{ email }} 中查收!</p>
{% endif %}
</body>
</html>
5)email_send
【密码重置】发送邮件的相关代码与用户注册账号激活整合在一起,详见用户注册账号激活 --发送邮件
3.4.2 重置密码
1)forms.py:
class ModifyPwdForm(forms.Form):
"""重置密码"""
password1 = forms.CharField(required=True, min_length=6)
password2 = forms.CharField(required=True, min_length=6)
def clean_password1(self):
if self.cleaned_data.get('password1').isdigit() or self.cleaned_data.get('password1').isalpha():
raise ValidationError('密码必须包含数字和字母')
else:
return self.cleaned_data['password1']
2)views.py:
# 确定当前用户邮箱是否正确,如正确转到重置密码页面,进行密码重置
def pwd_reset(request,ac_code):
"""用户重置密码链接"""
if request.method =="GET":
records = EmailVerifyRecord.objects.filter(code=ac_code)
if records:
email = records[0].email
return render(request, "password_reset.html", {"email": email})
else:# 链接不对
return render(request, "active_fail.html")
# 重置密码
def modify_pwd(request):
"""重置密码"""
if request.method == "POST":
modify_form = forms.ModifyPwdForm(request.POST)
if modify_form.is_valid():
pwd1 = request.POST.get("password1", None)
pwd2 = request.POST.get("password2", None)
# email数据是从pwd_reset获取到的
email = request.POST.get("email", None)
if pwd1 != pwd2:
return render(request, "password_reset.html", {"email": email, "msg": "密码不一致!"})
user = UserProfile.objects.get(email=email)
user.password = make_password(pwd2)
user.save()
return render(request, "login.html")
else:
email = request.POST.get("email", None)
return render(request, "password_reset.html", {"email": email, "modify_form": modify_form})
else:
return render(request, 'password_reset.html')
3)urls.py配置
from django.urls import path
from web_online import views
urlpatterns = [
re_path(r'^reset/(\w+)/',views.pwd_reset,name='reset'), # 邮箱重置密码链接
path('modify_pwd/',views.modify_pwd,name='modify_pwd'), # 重置密码
]
4)HTML前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<title>密码修改</title>
<link rel="stylesheet" type="text/css" href="/static/css/reset.css">
<link rel="stylesheet" type="text/css" href="/static/css/animate.css">
<link rel="stylesheet" type="text/css" href="/static/css/style.css">
<body>
<div class="wp">
<div class="resetpassword" id="resetPwdForm">
<h1>修改密码</h1>
<p>已经通过验证,请设置新密码</p>
<form id="reset_password_form" action="{% url 'modify_pwd' %}" method="post">
{% csrf_token %}
<ul>
<li class="{% if modify_form.errors.password1.0 %} errorput {% endif %}">
<span class="">新 密 码 :</span>
<input type="password" name="password1" id="pwd" placeholder="6-20位非中文字符">
<i></i>
</li>
<input type="hidden" name="email" value="{{ email }}">
<li class="{% if modify_form.errors.password2.0 %} errorput {% endif %}">
<span class="">再次输入密码 :</span>
<input type="password" name="password2" id="repwd" placeholder="6-20位非中文字符">
<i></i>
</li>
<div class="error btns" id="jsPasswdResetTips" style="color: red">
{% for key,error in modify_form.errors.items %}{{ key }}:{{ error }}{% endfor %}
{{ msg }}
</div>
<li class="button">
<input type="submit" value="提交">
</li>
</ul>
</form>
</div>
<div class="resetpassword" id="reset_password_tips" style="display:none;">
<h1>修改密码成功,请重新登录</h1>
<img class="fl" src="/static/images/check2.png">
<p class="successword">已经成功修改密码,请重新登录</p>
</div>
</div>
</body>
</html>
未完待续。。
来源:https://www.cnblogs.com/Eric15/p/10216106.html
