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作用

  1. 生成随机字符串
  2. 写到用户浏览器的cookie中
  3. 保存到session中
  4. 在随机字符串对应的字典中设置相关内容

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>验&nbsp;证&nbsp;码</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
forms.py

  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">密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;码</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" />&nbsp;自动登录</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>邮&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;箱</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>密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;码</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>验&nbsp;证&nbsp;码</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>
register.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>
send_email_success.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
email_send.py

 

   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>
active_success.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p style="color: red;">链接已经失效</p>
</body>
</html>
active_fail.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>用&nbsp;户&nbsp;名</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>密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;码</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>
login.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">密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;码</span>
            <input type="password" name="password" id="jsResetPwd" placeholder="请输入新密码"/>
        </div>
        <div class="box">
            <span class="word2">确&nbsp;认&nbsp;密&nbsp;码</span>
            <input type="password" name="password2" id="jsResetPwd2" placeholder="请再次输入新密码"/>
        </div>
        <div class="box">
            <span class="word2">验&nbsp;&nbsp;证&nbsp;&nbsp;码</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>帐&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;号</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>验&nbsp;证&nbsp;码</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>
forgetpwd.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>
send_email_success.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>
password_reset.html


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

未完待续。。

 

posted on 2019-01-03 18:15  Eric_nan  阅读(890)  评论(0编辑  收藏  举报