Django web 开发(四) - Django项目实践(七)-账户登录

账户登录

登录界面

修改myproject/employee_management/templates/layout.html

{% block css %}
<style>

    .navbar {
      
      
        border-radius: 0;
    }

</style>

{% endblock %}

在这里插入图片描述
新建myproject/employee_management/templates/login.html

{% extends 'layout.html' %}


{% block css %}
<style>
    .account {
      
      
        width: 400px;
        border: 1px solid #dddddd;
        border-radius: 5px;
        box-shadow: 5px 5px 20px #aaa;

        margin-left: auto;
        margin-right: auto;
        margin-top: 100px;
        padding: 20px 40px;
    }

    .account h2 {
      
      
        margin-top: 10px;
        text-align: center;
    }
</style>
{% endblock %}


{% block content %}
<div class="account">
    <h2>用户登录</h2>
    <div class="panel-body">
        <form method="post">
            {% csrf_token %}
            <div class="form-group">
                <label>用户名</label>
                <input type="text" class="form-control" placeholder="用户名" name="username">
            </div>
            <div class="form-group">
                <label>密码</label>
                <input type="password" class="form-control" placeholder="密码" name="password">
            </div>
            <button type="submit" class="btn btn-primary center-block" style="width: 80px;">登录</button>
        </form>
    </div>
</div>
{% endblock %}

修改myproject/myproject/urls.py

# 登录
path('login/', account.login),

新建myproject/employee_management/views/account.py

from django.shortcuts import render

def login(request):
    """登录"""

    return render(request, 'login.html')

浏览器简单访问
在这里插入图片描述

用户名密码验证

修改myproject/employee_management/templates/login.html

{% extends 'layout.html' %}


{% block css %}
<style>
    .account {
      
      
        width: 400px;
        border: 1px solid #dddddd;
        border-radius: 5px;
        box-shadow: 5px 5px 20px #aaa;

        margin-left: auto;
        margin-right: auto;
        margin-top: 100px;
        padding: 20px 40px;
    }

    .account h2 {
      
      
        margin-top: 10px;
        text-align: center;
    }
</style>
{% endblock %}


{% block content %}
<div class="account">
    <h2>用户登录</h2>
    <div class="panel-body">
        <form method="post" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label>用户名</label>
                {
   
   { form.username }}
                <span style="color: red;">{
   
   { form.errors.username.0 }}</span>
                
            </div>
            <div class="form-group">
                <label>密码</label>
                {
   
   { form.password }}
                <span style="color: red;">{
   
   { form.errors.password.0 }}</span>
            </div>
            
            <button type="submit" class="btn btn-primary center-block" style="width: 80px;">登录</button>
        </form>
    </div>
</div>
{% endblock %}

修改myproject/employee_management/views/account.py

from django.shortcuts import render, HttpResponse
from django import forms
from employee_management.utils.modelform import BootStrapForm
from employee_management.utils.encrypt import md5
from employee_management.models import Admin

# 这一次不使用ModelForm,使用Form来实现
class LoginForm(BootStrapForm):
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput(attrs={
    
    "class": "form-control"}),
        required=True,
    )
    password = forms.CharField(
        label="用户名",
        # render_value=True 表示当提交后,如果密码输入错误,不会自动清空密码输入框的内容
        widget=forms.PasswordInput(attrs={
    
    "class": "form-control"}, ),
        required=True,
    )

    def clean_password(self):
        pwd = self.cleaned_data.get("password")
        return md5(pwd)


def login(request):
    """登录"""
    if request.method == "GET":
        form = LoginForm()
        return render(request, 'login.html', {
    
    "form": form})

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 验证成功, 获取到的用户名和密码
        # print(form.cleaned_data)
        # {'username': '123', 'password': '123'}
        # {'username': '456', 'password': '0f54af32f41a5ba8ef3a2d40cd6ccf25'}

        # 去数据库校验用户名和密码是否正确
        admin_object = Admin.objects.filter(**form.cleaned_data).first()
        # 如果数据库中没有查询到数据
        if not admin_object:
        	# 手动抛出错误显示在"password"字段下
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {
    
    "form": form})
        return HttpResponse("登陆成功")

    return render(request, 'login.html', {
    
    "form": form})

浏览器进行测试
新建用户toker(必须为后来使用md5加密过密码的用户,不能是明文密码的用户)
在这里插入图片描述
输入错误的密码
在这里插入图片描述
输入正确的密码
在这里插入图片描述

配置cookie与session

修改myproject/employee_management/views/account.py

def login(request):
    """登录"""
    if request.method == "GET":
        form = LoginForm()
        return render(request, 'login.html', {
    
    "form": form})

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 验证成功, 获取到的用户名和密码
        print(form.cleaned_data)
        # {'username': '123', 'password': '123'}
        # {'username': '456', 'password': '0f54af32f41a5ba8ef3a2d40cd6ccf25'}

        # 去数据库校验用户名和密码是否正确
        admin_object = Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {
    
    "form": form})

        # 如果用户名密码正确
        # 网站生成随机字符创,写到用户浏览器的cookie中,再写入到服务器的session中
        request.session["info"] = {
    
    'id': admin_object.id, 'name': admin_object.username}
        return redirect("/admin/list/")

    return render(request, 'login.html', {
    
    "form": form})

在这里插入图片描述
登录成功后,跳转到管理员列表界面
session信息保存在了服务器中的Mysql数据库django_session表中

mysql> use my_project;

mysql> show tables;
+--------------------------------+
| Tables_in_my_project           |
+--------------------------------+
| auth_group                     |
| auth_group_permissions         |
| auth_permission                |
| auth_user                      |
| auth_user_groups               |
| auth_user_user_permissions     |
| django_admin_log               |
| django_content_type            |
| django_migrations              |
| django_session                 |
| employee_management_admin      |
| employee_management_department |
| employee_management_prettynum  |
| employee_management_userinfo   |
+--------------------------------+

mysql> select * from django_session;
+----------------------------------+-------------------------------------------------------------------------------------------------+----------------------------+
| session_key                      | session_data                                                                                    | expire_date                |
+----------------------------------+-------------------------------------------------------------------------------------------------+----------------------------+
| zkgq26t7hqx3yu6xo04bws856002n5aj | eyJpbmZvIjp7ImlkIjoxMiwibmFtZSI6InRva2VyIn19:1pElim:Tus2mHaJUTNTfzhppuah8N0FVdLXQxyvRk_4n-4fP6g | 2023-01-23 06:33:24.373104 |
+----------------------------------+-------------------------------------------------------------------------------------------------+----------------------------+

在这里插入图片描述

当实现了这个功能后,我们需要对每个页面做验证,只有登陆的人才可以访问这些页面,这个功能在下一节的中间件中来实现

中间件实现登录验证

中间件会在视图函数下的每个方法执行前调用,不用在每个方法下面进行判断,不然函数太多,过于繁琐。

新建myproject/employee_management/middleware目录
在其下新建myproject/employee_management/middleware/auth.py文件

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect


class AuthMiddleware(MiddlewareMixin):

    def process_request(self, request):

        # 0.排除不需要的页面
        if request.path_info == "/login/":
            return

        # 1.读取当前访问的用户的session信息,如果能读到,说明已登录过,就可以继续向后走
        info_dict = request.session.get("info")
        if info_dict:
            return

        # 2.如果没有登录信息
        return redirect("/login/")

修改myproject/myproject/urls.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'employee_management.middleware.auth.AuthMiddleware',
]

在这里插入图片描述
浏览器访问/pretty/list/进行测试
你会发现只要你没登录过,不管你访问什么页面,都会跳转到login登录界面

登录校验的功能算是实现了,但是登录的用户目前无法退出,下一节进行介绍

用户注销

小插曲: 我刚才整理代码的时候才发现,我因为之前使用了datetimepicker的日期插件,粘贴代码的时候,将我原来css样式给覆盖掉了,其实是我粘贴多了,需要先改一下,不然右上角的按钮无法展开,抱歉抱歉

在这里插入图片描述
修改myproject/employee_management/templates/layout.html,将下面的几行注释掉
在这里插入图片描述
配置注销按钮的跳转URL地址
在这里插入图片描述

然后保存就可以了

接下来实现注销功能

修改myproject/employee_management/views/account.py

def logout(request):
    """ 注销 """

    # 清楚当前session
    request.session.clear()

    return redirect("/login/")

修改myproject/myproject/urls.py

path('logout/', account.logout),

浏览器测试
在这里插入图片描述
在这里插入图片描述登录成功后,右上角的用户名还不是目前登录的用户,所以我们还需要再做一次修改
在这里插入图片描述
因为在login函数request.session中定义的username字段对应的名称为name,所以我们可以在前端代码中直接调用

<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{
   
   { request.session.info.name }}<span class="caret"></span></a>

在这里插入图片描述
在这里插入图片描述

图片验证码

生成验证码参考链接: https://www.cnblogs.com/wupeiqi/articles/5812291.html

下载必要的软件

pip3 install pillow

在这里插入图片描述
下载字体

点击这里: 验证码字体下载

新建myproject/employee_management/utils/ttf目录,将字体放在其下
在这里插入图片描述
新建myproject/employee_management/utils/code.py,编辑验证码生成函数

from PIL import Image, ImageDraw, ImageFilter, ImageFont
import random
 

def check_code(width=120, height=30, char_length=5, font_file='employee_management/utils/ttf/Monaco.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
    draw = ImageDraw.Draw(img, mode='RGB')
 
    def rndChar():
        """
        生成随机字母   
        :return:
        """
        return chr(random.randint(65, 90))
 
    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))
 
    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for i in range(char_length):
        char = rndChar()
        code.append(char)
        h = random.randint(0, 4)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
 
    # 写干扰点
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
 
    # 写干扰圆圈
    for i in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        x = random.randint(0, width)
        y = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())
 
    # 画干扰线
    for i in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)
 
        draw.line((x1, y1, x2, y2), fill=rndColor())
 
    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img,''.join(code)


if __name__ == '__main__':
    img, code_str = check_code()
    print(code_str)

    with open('code.png', 'wb') as f:
        img.save(f, format='png')

修改myproject/employee_management/views/account.py,引入调用刚刚写的验证码生成函数

from employee_management.utils.code import check_code
from django.shortcuts import HttpResponse
from io import BytesIO

def image_code(request):
    """ 生成图片验证码 """
    # 调用pillow函数,生成图片
    img, code_string = check_code()

	# 将图片保存到内存
    stream = BytesIO()
    img.save(stream, 'png')
    return HttpResponse(stream.getvalue())

修改myproject/myproject/urls.py

path('image/code/', account.image_code),

还需要将该函数的URL加入访问白名单,使验证码链接可用
编辑myproject/employee_management/middleware/auth.py

# 0.排除不需要的页面
if request.path_info in ["/login/", "/image/code/"]:
    return

修改myproject/employee_management/templates/login.html

{% extends 'layout.html' %}


{% block css %}
<style>
    .account {
      
      
        width: 400px;
        border: 1px solid #dddddd;
        border-radius: 5px;
        box-shadow: 5px 5px 20px #aaa;

        margin-left: auto;
        margin-right: auto;
        margin-top: 100px;
        padding: 20px 40px;
    }

    .account h2 {
      
      
        margin-top: 10px;
        text-align: center;
    }
</style>
{% endblock %}


{% block content %}
<div class="account">
    <h2>用户登录</h2>
    <div class="panel-body">
        <form method="post" novalidate>
            {% csrf_token %}
            <div class="form-group">
                <label>用户名</label>
                {
   
   { form.username }}
                <span style="color: red;">{
   
   { form.errors.username.0 }}</span>
                
            </div>
            <div class="form-group">
                <label>密码</label>
                {
   
   { form.password }}
                <span style="color: red;">{
   
   { form.errors.password.0 }}</span>
            </div>
            <div class="form-group">
                <label for="id_code">图片验证码</label>
                <div class="row">
                    <div class="col-xs-7">
                        <input type="text" name="code" class="form-control" placeholder="请输入图片验证码" required="" id="id_code">
                        <span style="color: red;"></span>
                    </div>
                    <div class="col-xs-5">
                        <img src="/image/code/" alt="" id="image_code">
                    </div>
                </div>
            </div>
            <button type="submit" class="btn btn-primary center-block" style="width: 80px;">登 录</button>
        </form>
    </div>
</div>
{% endblock %}

在这里插入图片描述
浏览器访问
在这里插入图片描述

验证码的校验

接下来需要验证用户输入的验证码与生成的验证码是否一致

修改myproject/employee_management/views/account.py

  • 第一处
code = forms.CharField(
    label="验证码",
    widget=forms.TextInput(attrs={
    
    "class": "form-control"}),
    required=True,
)

在这里插入图片描述

  • 第二处
def image_code(request):
    """ 生成图片验证码 """
    # 调用pillow函数,生成图片
    img, code_string = check_code()

    # 写入到自己的session中,以便于后续获取验证码再进行校验
    request.session['image_code'] = code_string
    # 给session设置 60s 超时
    request.session.set_expiry(60)

    stream = BytesIO()
    img.save(stream, 'png')
    return HttpResponse(stream.getvalue())

在这里插入图片描述

  • 第三处
def login(request):
    """登录"""
    if request.method == "GET":
        form = LoginForm()
        return render(request, 'login.html', {
    
    "form": form})

    form = LoginForm(data=request.POST)
    if form.is_valid():
        # 验证成功, 获取到的用户名和密码
        print(form.cleaned_data)
        # {'username': '123', 'password': '123'}
        # {'username': '456', 'password': '0f54af32f41a5ba8ef3a2d40cd6ccf25'}

        # 验证码的校验
        user_input_code = form.cleaned_data.pop('code')
        image_code = request.session.get('image_code', "")
        print("user_input_code={}, image_code={}".format(user_input_code, image_code))
        if image_code.upper() != user_input_code.upper():
            form.add_error("code", "验证码错误")
            return render(request, 'login.html', {
    
    "form": form})

        # 去数据库校验用户名和密码是否正确
        admin_object = Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {
    
    "form": form})

        # 如果用户名密码和验证码正确
        # 网站生成随机字符创,写到用户浏览器的cookie中,再写入到服务器的session中
        request.session["info"] = {
    
    'id': admin_object.id, 'name': admin_object.username}
        # 重新设置session的超时时间,因为之前设置的session的超时时间的 60s
        request.session.set_expiry(60 * 60 * 24)

        return redirect("/admin/list/")

    return render(request, 'login.html', {
    
    "form": form})

在这里插入图片描述
浏览器登录测试
在这里插入图片描述
在这里插入图片描述

现在已经可以正常登录,但是还可以进行优化
我们想要点击验证码图片进行刷新验证码,很简单

修改myproject/employee_management/templates/login.html

<img src="/image/code/" alt="" id="image_code" onclick="this.setAttribute('src','/image/code/?random='+Math.random())">

在这里插入图片描述
点击验证码图片后,验证码可刷新

猜你喜欢

转载自blog.csdn.net/qq_43139145/article/details/128716301