Django学习记录——管理员-登录注销的实现

1.管理员案例

1.1管理员数据库

1.1.1 表结构

在这里插入图片描述

1.1.2 管理员表的建立

class Admin(models.Model):
    """管理员表"""
    username = models.CharField(max_length=32, verbose_name="用户名")
    password = models.CharField(max_length=64, verbose_name="密码")

1.2 管理员数据的管理

1.2.1 管理员列表的展示

admin_list.html

{% extends 'layout.html' %}

{% block content %}
    <div style="margin-bottom: 10px">
        <a type="button" class="btn btn-success" href="/admin/add"><span class="glyphicon glyphicon-plus-sign"
                                                                         aria-hidden="true"></span> 添加管理员</a>
        <div style="width: 300px; float: right">

            <form method="get" action="/admin/list">
                <div class="input-group">
                    <input type="text" class="form-control" name="username" placeholder="Search for..."
                           value="{{ username }}">
                    <span class="input-group-btn">
                        <button class="btn btn-default" type="submit"><span class="glyphicon glyphicon-search"
                                                                            aria-hidden="true"></span></button>
                    </span>
                </div>
            </form>

        </div>

    </div>
    <div class="panel panel-default">
        <div class="panel-heading">管理员列表</div>
        <div class="bs-example" data-example-id="hoverable-table">
            <table class="table table-hover">
                <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>密码</th>
                    <th>操作</th>
                </tr>
                </thead>
                <tbody>
                {% for obj in queryset %}
                    <tr>
                        <td>{{ obj.id }}</td>
                        <td>{{ obj.username }}</td>
                        <td>******</td>
                        <td>
                            <a class="btn btn-success" href="/admin/{{ obj.id }}/reset_password">重置密码</a>
                            <a class="btn btn-warning btn-sm" href="/admin/{{ obj.id }}/update">编辑</a>
                            <a class="btn btn-danger btn-sm" href="/admin/{{ obj.id }}/del">删除</a>
                        </td>
                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </div>
        <ul class="pagination">
            {{ page_str }}
        </ul>

    </div>
{% endblock %}

admin.py

def admin_list(request):
    """管理员列表"""
    # 搜索
    data_dict = {}
    username = request.GET.get('username', "")
    if username:
        # {关键字: 范围, }
        data_dict['username__contains'] = username
    # data_dict = {'username__contains': 'Y'}
    queryset = models.Admin.objects.filter(**data_dict)

    # 分页功能
    page_object = Pagination(request, queryset)
    context = {
        "queryset": page_object.query_set,
        "page_str": page_object.createHtml()
    }
    return render(request, "admin_list.html", context)
  • 搜索功能

{关键字:范围} 利用字典作为搜索的条件集合

# 搜索
    data_dict = {}
    username = request.GET.get('username', "")
    if username:
        # {关键字: 范围, }
        data_dict['username__contains'] = username
    # data_dict = {'username__contains': 'Y'}
    queryset = models.Admin.objects.filter(**data_dict)
  • 分页功能
page_object = Pagination(request, queryset)
context = {
    "queryset": page_object.query_set,
    "page_str": page_object.createHtml()
}

1.2.2 管理员的添加

admin.py

def admin_add(request):
    if request.method == "GET":
        form = adminModelForm()
        return render(request, "admin_add.html", {"form": form})
    else:
        form = adminModelForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("/admin/list")
        else:
            return render(request, "admin_add.html", {"form": form})

form = adminModelForm()adminModelForm()中创建表单的样式并且规定一些校验规则

  • 表单样式创建
class adminModelForm(BootStrapModelForm):
    # 新增一个确认密码字段
    confirm_password = forms.CharField(
        max_length=64,
        label='确认密码',
        widget=forms.PasswordInput  # 密码格式
        # 加上后,若校验失败,密码不会清空
        # widget=forms.PasswordInput(render_value=True)
    )

    class Meta:
        model = models.Admin
        fields = ['username', 'password', 'confirm_password']
        # 额外给password增加密码输入框的样式
        widgets = {
            'password': forms.PasswordInput
            # 加上后,若校验失败,密码不会清空
            # widget=forms.PasswordInput(render_value=True)
        }
class BootStrapModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        for key, value in self.fields.items():
            value.widget.attrs = {'class': 'form-control', "placeholder": value.label}
  • 新建的管理员名称不允许重复(钩子函数实现
def clean_username(self):
    username = self.cleaned_data.get('username')
    # 判断管理员是否存在
    flag = models.Admin.objects.filter(username=username).exists()
    if flag:
        raise ValidationError("该管理员已存在")
    return username
  • 确认两次输入密码一致

    • md5的应用

    encrypt.py

    def md5(data_string):
        # 导入django自带的sault
        obj = hashlib.md5(settings.SECRET_KEY.encode('utf-8'))
        obj.update(data_string.encode('utf-8'))
        return obj.hexdigest()
    
    • 钩子函数实现判断规则
    # 钩子函数对密码进行md5加密
    def clean_password(self):
        password = self.cleaned_data.get('password')
        return md5(password)  # md5加密并password返回该值
    
    # 钩子函数校验确认密码
    def clean_confirm_password(self):
        # txt_password 表示用户输入的md5加密完的密码
        # txt_confirm_password 表示用户输入的确认密码
        txt_password = self.cleaned_data.get('password')
        txt_confirm_password = self.cleaned_data.get('confirm_password')
        # md5_confirm_password 表示用户输入的md5加密后的确认密码
        md5_confirm_password = md5(txt_confirm_password)
    
        if txt_password == md5_confirm_password:
            # 校验通过(密码一致),返回输入的数据
            # 对于此案例,不保存confirm_password字段,因此用处不大
            return md5_confirm_password
        else:
            raise ValidationError("密码不一致")
    

forms.py

class adminModelForm(BootStrapModelForm):
    # 新增一个确认密码字段
    confirm_password = forms.CharField(
        max_length=64,
        label='确认密码',
        widget=forms.PasswordInput  # 密码格式
        # 加上后,若校验失败,密码不会清空
        # widget=forms.PasswordInput(render_value=True)
    )

    class Meta:
        model = models.Admin
        fields = ['username', 'password', 'confirm_password']
        # 额外给password增加密码输入框的样式
        widgets = {
            'password': forms.PasswordInput
            # 加上后,若校验失败,密码不会清空
            # widget=forms.PasswordInput(render_value=True)
        }
    def clean_username(self):
        username = self.cleaned_data.get('username')
        # 判断管理员是否存在
        flag = models.Admin.objects.filter(username=username).exists()
        if flag:
            raise ValidationError("该管理员已存在")
        return username

    # 钩子函数对密码进行md5加密
    def clean_password(self):
        password = self.cleaned_data.get('password')
        return md5(password)  # md5加密并password返回该值

    # 钩子函数校验确认密码
    def clean_confirm_password(self):
        # txt_password 表示用户输入的md5加密完的密码
        # txt_confirm_password 表示用户输入的确认密码
        txt_password = self.cleaned_data.get('password')
        txt_confirm_password = self.cleaned_data.get('confirm_password')
        # md5_confirm_password 表示用户输入的md5加密后的确认密码
        md5_confirm_password = md5(txt_confirm_password)

        if txt_password == md5_confirm_password:
            # 校验通过(密码一致),返回输入的数据
            # 对于此案例,不保存confirm_password字段,因此用处不大
            return md5_confirm_password
        else:
            raise ValidationError("密码不一致")

1.2.3管理员的删除

def admin_del(request, nid):
    models.Admin.objects.filter(id=nid).delete()
    return redirect("/admin/list")

1.2.4 管理员的更新

views.py

def admin_update(request, nid):
    row_object = models.Admin.objects.filter(id=nid).first()
    # 先判断删除的id数据库是否存在
    if not row_object:
        return redirect("/admin/list")
    title = "管理员编辑"
    if request.method == "GET":
        # instance=row_object将查询到待修改的数据填充到表单
        form = admin_editModelForm(instance=row_object)

        return render(request, "change.html", {"form": form, "title": title})
    # instance=row_object 更新后,覆盖原有数据
    form = admin_editModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect("/admin/list")
    return render(request, "change.html", {"form": form, "title": title})

1.2.5 管理员密码的重置

views.py

def admin_reset_password(request, nid):
    row_object = models.Admin.objects.filter(id=nid).first()
    title = "重置密码--{}".format(row_object.username)
    if not row_object:
        return redirect("/admin/list")
    if request.method == "GET":
        form = admin_reset_password_ModelForm()
        return render(request, "change.html", {"form": form, "title": title})

    form = admin_reset_password_ModelForm(data=request.POST, instance=row_object)
    if form.is_valid():
        form.save()
        return redirect("/admin/list")
    return render(request, "change.html", {"form": form, "title": title})

校验规则(钩子函数)

admin_reset_password_ModelForm中实现

  • 重置的密码不能与原来密码一致
def clean_password(self):
    password = self.cleaned_data.get('password')
    MD5_pwd = md5(password)
    #  form = admin_reset_password_ModelForm(data=request.POST, instance=row_object)
    # 此句话为调用admin_reset_password_ModelForm返回的表单
    # 利用 self.instance.pk 可以获取到row_object该行数据的id
    # 判断该id对应的密码是否为输入的密码(比较的是加密完的密码)
    result = models.Admin.objects.filter(id=self.instance.pk,password=MD5_pwd).exists()
    if result:
        raise ValidationError("不能与原密码一致!")
    return MD5_pwd  # md5加密并password返回该值
  • 两次输入的密码应一致
def clean_confirm_password(self):
    # txt_password 表示用户输入的md5加密完的密码
    # txt_confirm_password 表示用户输入的确认密码
    txt_password = self.cleaned_data.get('password')
    txt_confirm_password = self.cleaned_data.get('confirm_password')
    # md5_confirm_password 表示用户输入的md5加密后的确认密码
    md5_confirm_password = md5(txt_confirm_password)

    if txt_password == md5_confirm_password:
        # 校验通过(密码一致),返回输入的数据
        # 对于此案例,不保存confirm_password字段,因此用处不大
        return md5_confirm_password
    else:
        raise ValidationError("密码不一致")

1.3 更新页面的整合

各个部分的更新页面都差不多,因此对页面进行整合为change.html

change.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.css' %}">
    <style>
        .navbar {
            border-radius: 0;
        }
    </style>
    {% block css %}{% endblock %}
</head>
<body>
<nav class="navbar navbar-default">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">管理系统</a>
        </div>

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="/admin/list">管理员管理</a></li>
                <li><a href="/depart/list">部门管理</a></li>
                <li><a href="/user/list">员工管理</a></li>
                <li><a href="/number/list">靓号管理</a></li>
            </ul>

            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">登录</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">admin<span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人中心</a></li>
                        <li><a href="#">设置</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">注销</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>
<div class="container">

    <div class="panel panel-default">
        <div class="panel-heading">{{ title }}</div>
        <div class="panel-body">
            <div class="bs-example" data-example-id="simple-form-inline">
                <form class="form" method="post" novalidate>
                    {% csrf_token %}
                    {#创建表单,form为userinfo各字段的表单#}
                    {#每一个field都是一个字段的输入框#}

                    {% for field in form %}
                        <div class="form-group">
                            <label>{{ field.label }}</label>
                            {{ field }}
                            <span style="color: red">{{ field.errors.0 }}</span>
                            {# field.errors.0显示第一条错误即可 #}
                        </div>
                    {% endfor %}
                    <input type="submit" class="btn btn-success" value="提交">

                </form>
            </div>
        </div>
    </div>

</div>

<script src="{% static 'js/jquery-3.7.1.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-datepicker/js/bootstrap-datepicker.js' %}"></script>
<script src="{% static 'plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js' %}"></script>


<script>
    $(function () {
        $('#dt_creat_time').datepicker({
            format: 'yyyy-mm-dd',
            //startDate: '0',//最早日期为当前日期,无法wangqian
            language: "zh-CN",
            autoclose: true
        });

    })
</script>

{% block js %}{% endblock %}
</body>
</html>

{% block content %}

{% endblock %}

固定参数

  • 表单标题
<div class="panel-heading">{{ title }}</div>

从每个操作的视图函数中传进来

  • 表单
    form为view函数创建好的表单标签(包含input输入框、样式、value值)
<div class="panel-body">
            <div class="bs-example" data-example-id="simple-form-inline">
                <form class="form" method="post" novalidate>
                    {% csrf_token %}
                    {#创建表单,form为userinfo各字段的表单#}
                    {#每一个field都是一个字段的输入框#}

                    {% for field in form %}
                        <div class="form-group">
                            <label>{{ field.label }}</label>
                            {{ field }}
                            <span style="color: red">{{ field.errors.0 }}</span>
                            {# field.errors.0显示第一条错误即可 #}
                        </div>
                    {% endfor %}
                    <input type="submit" class="btn btn-success" value="提交">

                </form>
            </div>
        </div>
  • form标签中的action

    对于form中的action关键字,指定了表单传数据的目的地,若不写默认form变量的来源位置

    使用默认位置,不仅简便,而且便于整合,不用额外在action中拼接nid

2. 登录页面

2.1登陆功能的实现

login.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.css' %}">
    <link rel="stylesheet" href="{% static 'plugins/bootstrap-datepicker/css/bootstrap-datepicker.css' %}">
    <style>
        .login {
            position: fixed;
            width: 400px;
            height: 300px;
            border: 1px solid #adadad;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            margin: auto;
            padding: 20px 10px;
            box-shadow: 2px 2px 5px #8c8c8c;
        }

        .font {
            text-align: center;
        }
    </style>
</head>
<body>
<div class="login">
    <form method="post">
        {% csrf_token %}
        <div class="font"><h1>用户登录</h1></div>
        <div class="form-group">
            {% for obj in form %}
                <label>{{ obj.label }}</label>
                {{ obj }}
                <span style="color: red">{{ obj.errors.0 }}</span>
            {% endfor %}
        </div>
        <button type="submit" class="btn btn-success">登录</button>
    </form>
</div>
</body>
</html>

login.py

def login(request):
    if request.method == 'GET':
        form = login_ModelForm()
        return render(request, 'login.html', {'form': form})
    form = login_ModelForm(request.POST)
    # 将输入的用户名密码与数据库的用户名密码进行比对
    if form.is_valid():
        dic = form.cleaned_data
        # 判断用户名密码是否正确(是否查到)
        admin_object = models.Admin.objects.filter(**dic).first()
        if not admin_object:
            # 在form中添加错误(错误地方,错误)   '
            # 将错误信息展示到密码上
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {'form': form})
        # 验证成功——开始cookie验证
        # request.session['info'] = admin_object.username
        # 以下这句话,完成了以下功能
        """
        1.生成cookie存储到浏览器中
        2.将cookie和浏览器的信息存储到session中(django存到数据库中)
            session主要存储   key(cookie)   data(根据下面数据形成的)  日期等等
        """
        request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
        return redirect('/admin/list')
    return render(request, 'login.html', {'form': form})
  • login_ModelForm()表单类
    BootStrapModelForm 对表单的样式进行更改
    实现了表单的建立和对输入密码进入md5加密,便于与数据库比较
class login_ModelForm(BootStrapModelForm):
  class Meta:
      model = models.Admin
      fields = ['username', 'password']

  # 将输入的密码进行md5,加密
  def clean_password(self):
      password = self.cleaned_data.get('password')
      MD5_pwd = md5(password)
      return MD5_pwd
  • 获取表单数据的方法

  • form.cleaned_data.get(xxx)

    该方法能获取对应name的数据,返回一个字符串

username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
res = models.Admin.objects.filter(username=username,password=password).first()
  • form.cleaned_data

    该方法返回表单中所有的输入数据,将name与数据以字典的形式对应返回

# 返回值的字典形式可以直接传入查询数据库的条件
dic = form.cleaned_data
# 判断用户名密码是否正确(是否查到)
admin_object = models.Admin.objects.filter(**dic).first()
  • 错误信息的提示

    对于用户名与密码不匹配的情况,要显示错误提示并重新登录

    • 判断过程中需要获取输入的数据以及识别失败返回登陆页面,因此需要在视图函数中判断,而不能在钩子函数实现(不匹配时需返回登陆页面)
    • 判断表单是否返回数据(表单是否为空)
admin_object = models.Admin.objects.filter(**dic).first()
if not admin_object:
   form.add_error("password", "用户名或密码错误")
   return render(request, 'login.html', {'form': form})
request.session['info']={'id':admin_object.id,'username': admin_object.username}

form.add_error("password", "用户名或密码错误") 参数1表示错误信息显示的位置 参数2表示显示的错误信息 该语句新增错误信息到form的errors中,便于提示错误信息

使用位置login.html

<div class="login">
   <form method="post">
       {% csrf_token %}
       <div class="font"><h1>用户登录</h1></div>
       <div class="form-group">
           {% for obj in form %}
               <label>{{ obj.label }}</label>
               {{ obj }}
               <span style="color: red">{{ obj.errors.0 }}</span>
           {% endfor %}
       </div>
       <button type="submit" class="btn btn-success">登录</button>
   </form>
</div>

2.2 cookie与session

2.2.1 基本介绍

  • 短连接
http://127.0.0.1:8000/admin/list/
https://127.0.0.1:8000/admin/list/

对于http来说,建立无状态短连接(浏览器发送请求,后端网页接收请求,并响应请求,完成这个过程后断开连接)

在这里插入图片描述

实现浏览器与后端网页长久通信

​ 当浏览器向后端网页发送请求,后端网页向浏览器发送一个响应的过程中,后端网页给该浏览器生成一个为一个标识码**(字典的形式)来代表该浏览器,该标识码存储在该浏览器内,这个标识码就叫做cookie**;在后端网页给浏览器生成发送标识码时,也将该标识码与对应的浏览器存储在后端网页的session中;在之后的连接中,浏览器直接向后端网页发送cookie标识,在后端网页的session中查询是否存在,从而确定是否连接。

在这里插入图片描述

  • cookie:随机字符串(用来标识)
  • session:后端网页存储用户信息的部分,主要形式是数据库、redis、文件(django主要是数据库形式)
def login(request):
    if request.method == 'GET':
        form = login_ModelForm()
        return render(request, 'login.html', {'form': form})
    form = login_ModelForm(request.POST)
    if form.is_valid():件
        dic = form.cleaned_data
        # 判断用户名密码是否正确(是否查到)
        admin_object = models.Admin.objects.filter(**dic).first()
        if not admin_object:
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {'form': form})
        request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
        return redirect('/admin/list')
    return render(request, 'login.html', {'form': form})

当用户名与密码匹配成功后,实现浏览器与后端网页长久通信(验证cookie)

request.session['info']={'id': admin_object.id, 'username': admin_object.username}

以下这句话,完成了以下功能:

  1. 将后端网页生成cookie存储到浏览器中

  2. 将cookie和浏览器的信息存储到session中(django存到数据库中)、

session主要存储 key(cookie) data(根据下面数据形成的) 日期等等

生成的cookie存储到session的cookie部分
信息info 存储对session对应的data部分

2.2.2基本应用

# 验证成功,生成cookie和session,从session获取
# request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
info_dic = request.session.get('info')
# 若未登录,则info_dic为空  若登录,则info_dic为传入的数据
if not info_dic:
	return redirect('/login/')

在每一个视图函数中都加入该语句,直接视图函数时,判断是否有cookie(是否登录)

  • 若已登录,则对应浏览器生成cookie并且存取相应的数据信息到session中,根据获取某个浏览器在session中的数据来判断是否处于登录状态
  • 若未登录,直接跳转到登录页,不再继续执行视图函数

缺点

每一个视图函数均需要增添这些语句,非常冗余

2.2.3 中间件

2.2.3.1 中间件的原理

在这里插入图片描述

  • 中间件=django中的类

书写中间件(类的位置):app01-middleware-auth.py

引用的Django类:from django.utils.deprecation import MiddlewareMixin

中间件的注册:setting.py 中的MIDDLEWARE添加'app01.middleware.auth.M1'

  • 浏览器向django发送请求时,需要依次经过多个中间件后,视图函数才可以接收请求.请求顺序:浏览器-M1-M2-M3-视图函数

  • 视图函数响应请求时,也是依次经过中间件后,浏览器才可以接收到响应。响应顺序:视图函数-M3-M2-M1-浏览器

auth.py

class M1(MiddlewareMixin):
def process_request(self, request):
  print("m1-process_request")
  return 
  # return HttpResponse("无权访问")

def process_response(self, request, response):
  print("m1-process_response")
  return response


class M2(MiddlewareMixin):
def process_request(self, request):
  print("m2-process_request")
  return

def process_response(self, request, response):
  print("m2-process_response")
  return response

class M3(MiddlewareMixin):
def process_request(self, request):
  print("m3-process_request")
  return

def process_response(self, request, response):
  print("m3-process_response")
  return response

请求与响应顺序:

m1-process_request
m2-process_request
m3-process_request
m3-process_response
m2-process_response
m1-process_response
  • 若在依次请求中间件时,某个中间件直接返回响应,则其他中间件不在执行,直接响应浏览器

    用于实现登录校验,符合无返回值,不符合有返回值

    • 如果方法中没有返回值(返回None),继续向后走
    • 如果有返回值 HttpResponse、render 、redirect,则不再继续向后执行
class M1(MiddlewareMixin):
 def process_request(self, request):
     print("m1-process_request")
     return HttpResponse("无权访问")

 def process_response(self, request, response):
     print("m1-process_response")
     return response


class M2(MiddlewareMixin):
 def process_request(self, request):
     print("m2-process_request")
     return

 def process_response(self, request, response):
     print("m2-process_response")
     return response


class M3(MiddlewareMixin):
 def process_request(self, request):
     print("m3-process_request")
     return

 def process_response(self, request, response):
     print("m3-process_response")
     return response
m1-process_request
m1-process_response

中间件M1直接return HttpResponse("无权访问")  则不再执行其他中间,直接响应浏览器
2.2.3.2 中间件的登录校验
  • 中间件的设计
模板:
class AuthMiddleware(MiddlewareMixin):
    def process_request(self, request):
        ...
        ...
        ...
        return 

auth.py

class AuthMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 1.中间件只能检验出登陆视图函数的其他函数
        # request.path_info 获取当前用户请求的url  判断是否为登录视图的url
        # 如果是,不进行任何检验,直接进入登陆页面
        print(request.path_info)
        if request.path_info == '/login/':
            
        info_dic = request.session.get('info')
        if info_dic:
            return
        return redirect('/login')
  1. 中间件只能检验出登陆视图函数的其他函数
  • request.path_info 获取当前用户请求的url 判断是否为登录视图的url
    如果是登陆页面,不进行任何检验,直接进入登陆页面
  • 如果不是登录页面,则需要判断是否有登录信息
  1. 获取生成cookie时存的数据
  • 获取生成cookie时存的数据
  • 校验失败,直接返回,不允许进入视图函数

2.3 用户注销功能

删除存储的session信息,并返回登录页面即可

login.py

def logout(request):
    # 注销就是清除clear
    request.session.clear()
    return redirect('/login')

layout.html

...
...
...
<body>
<nav class="navbar navbar-default">

        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li><a href="/admin/list">管理员管理</a></li>
                <li><a href="/depart/list">部门管理</a></li>
                <li><a href="/user/list">员工管理</a></li>
                <li><a href="/number/list">靓号管理</a></li>
            </ul>

            <ul class="nav navbar-nav navbar-right">
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">{{ request.session.info.username }}<span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">个人中心</a></li>
                        <li><a href="#">设置</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="/logout">注销</a></li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</nav>
...
...
...

{{ request.session.info.username }}用户的登录信息存储在session中

来源:login视图函数中request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}

2.4 图片验证码功能

2.4.1 python生成图片

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


def check_code(width=120, height=30, char_length=5, font_file='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():
        # ASCII码对应字母
        return chr(random.randint(65, 90))

    # 生成随机颜色
    def rndColor():
        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__':
    # check_code()返回一张图片以及生成的随机字母
    img, code_str = check_code()
    print(code_str)
    # 保存图片
    with open('code.png', 'wb') as f:
        img.save(f, format='png')

2.4.2 验证码输入框与验证码图片在django项目的实现

  • 在登录的表单中添加验证码文本框
class login_ModelForm(BootStrapModelForm):
    password = forms.CharField(widget=forms.PasswordInput, label='密码')
    # 新增
    code = forms.CharField(widget=forms.TextInput, label='验证码', )

    class Meta:
        model = models.Admin
        # 新增
        fields = ['username', 'password', 'code']

    # 将输入的密码进行md5,加密
    def clean_password(self):
        password = self.cleaned_data.get('password')
        MD5_pwd = md5(password)
        return MD5_pwd
  • 登陆页面login.html
...
...
...

<div class="form-group">
    <label>{{ form.code.label }}</label>
    <div class="row">
        <div class="col-xs-7">
            {{ form.code }}
            <span style="color: red;font-size: 10px">{{ form.code.errors.0 }}</span>
        </div>
        <div class="col-xs-5">
            {#图片的地址直接转到生成图片的视图函数中#}
            {#特别注意:cookie验证时不仅要排除登录视图函数,还得排除生成图片视图函数#}
            <img id="img_code" src="/image/code" style="width: 125px">
        </div>
    </div>
</div>
...
...
...
  • 图片验证码的增加

    • 生成随机的图片验证码

    app-utils-code.py

    import random
    from PIL import Image, ImageDraw, ImageFont, ImageFilter
    
    def check_code(width=120, height=30, char_length=5, font_file='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 chr(random.randint(65, 90))
    
        # 生成随机颜色
        def rndColor():
            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)
    

    check_code()返回两个值

    img:<class 'PIL.Image.Image'>类型的图片

    code:图片验证码上的字符串

    • 图片验证码的显示
    <div class="col-xs-5">
    	<img id="img_code" src="/image/code" style="width: 125px">
    </div>
    

    图片的地址采用视图函数来实现,image_code视图函数自动接收生成的图片,加载内存中传入html中

    def image_code(request):
        img, code_string = check_code()
        request.session['image_code'] = code_string
        # 给session设置60s超时,超过60s,自动无效
        request.session.set_expiry(60)
        # 向内存传入图片
        stream = BytesIO()
        img.save(stream, 'png')
        return HttpResponse(stream.getvalue())
    
    1. 要点1

    img, code_string = check_code()调用pillow函数生成图片

    img:<class 'PIL.Image.Image'>类型的图片

    code_string为图片上的文字,用于校验

    1. 要点2

    request.session['image_code'] = code_string

    验证码的校验利用session 将图片码写入自己的session中,以便于后续获取验证码进行校验
    在session存储的数据也加一条 {image_code:code_string}

    1. 要点3

    request.session.set_expiry(60)给session设置60s超时,超过60s,自动无效

    1. 要点4
    stream = BytesIO()
    img.save(stream, 'png')
    return HttpResponse(stream.getvalue())
    

    向内存中传输图片,传入html中显示

图片的地址直接转到生成图片的视图函数中,特别注意:生成图片的视图函数也不需要cookie验证,cookie验证时不仅要排除登录视图函数,还得排除生成图片视图函数

auth.py

class AuthMiddleware(MiddlewareMixin):
    def process_request(self, request)
        if request.path_info in ['/login/','/image/code']:
            return
        # 2.获取生成cookie时存的数据
        info_dic = request.session.get('info')
        # 校验成功,允许进入视图函数
        if info_dic:
            return
        # 校验失败,直接返回,不允许进入视图函数
        return redirect('/login')

2.4.3 图片验证码的校验

已知在生成图片验证码时已经将图片的中的随机验证码字符串传入request.session['image_code'] = code_string,因此request.session中多存储一条code_string的信息。

如此,用户输入完验证码时,通过form.cleaned_data获取前端表单输入的数据,取出图片验证码code与session中的code_string进行对比即可

def login(request):
    if request.method == 'GET':
        form = login_ModelForm()
        return render(request, 'login.html', {'form': form})
    form = login_ModelForm(request.POST)
    
    if form.is_valid():
        # 用户输入的code 并且取出form.cleaned_data中的code,此时form.cleaned_data仅有username和password信息,使用户名和密码可以校验
        in_code= form.cleaned_data.pop("code")
        # 从session提取图片字母
        image_code = request.session.get('image_code','')
        
        # 判断验证码
        if not (in_code.upper()==image_code):
            # 在form中添加错误(错误地方,错误)   '
            # 将错误信息展示到密码上
            form.add_error("code", "验证码错误")
            return render(request, 'login.html', {'form': form})
        
        # 判断用户名密码是否正确(是否查到)
        admin_object = models.Admin.objects.filter(**form.cleaned_data).first()
        if not admin_object:
            form.add_error("password", "用户名或密码错误")
            return render(request, 'login.html', {'form': form})


        request.session['info'] = {'id': admin_object.id, 'username': admin_object.username}
        # cookie 保存7天
        request.session.set_expiry(60*60*60*7)
        return redirect('/admin/list')
    return render(request, 'login.html', {'form': form})

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/419344.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

韦东山嵌入式Liunx入门驱动开发三

文章目录 一、GPIO和Pinctrl子系统的使用1-1 Pinctrl子系统1-2 GPIO子系统1-3 基于GPIO子系统的LED驱动程序 本人学习完韦老师的视频&#xff0c;因此来复习巩固&#xff0c;写以笔记记之。 韦老师的课比较难&#xff0c;第一遍不知道在说什么&#xff0c;但是坚持看完一遍&…

如何在函数内部定义函数?

在Python中&#xff0c;您可以在一个函数内部定义另一个函数。这种情况下&#xff0c;内部函数的作用域仅限于外部函数&#xff0c;外部函数可以访问内部函数&#xff0c;但外部函数之外的代码无法访问内部函数。那么我们是编程游戏的时候出现一些函数定义的问题&#xff0c;应…

抖音视频批量下载软件|评论数据提取工具

我们团队研发的视频批量下载工具将为您带来全新的视频下载体验&#xff01;在市面上仅能通过单个视频链接提取的无水印工具已经无法满足您的需求&#xff0c;因此我们推出了这款功能强大的工具&#xff0c;不仅支持单个视频链接提取&#xff0c;还可以通过关键词进行视频搜索&a…

Windows WMI详解

WMI简介 WMI ( Windows Management Instrumentation, Windows管理规范)是Windows 2000/XP管理系统的核心&#xff0c;属于管理数据和操作的基础模块。设计WMI的初衷是达到一种通用性&#xff0c;通过WM操作系统、应用程序等来管理本地或者远程资源。它支持分布式组件对象模型(…

基于ssm课程管理系统

基于SSM的课程管理系统的设计与实现 摘 要 当下&#xff0c;正处于信息化的时代&#xff0c;许多行业顺应时代的变化&#xff0c;结合使用计算机技术向数字化、信息化建设迈进。以前学校对于课程信息的管理和控制&#xff0c;采用人工登记的方式保存相关数据&#xff0c;这种以…

【论文阅读笔记】Explicit Visual Prompting for Low-Level Structure Segmentations

1.介绍 Explicit Visual Prompting for Low-Level Structure Segmentations 低级结构分割的显式视觉提示 2023年发表在IEEE CVPR Paper Code 2.摘要 检测图像中低级结构&#xff08;低层特征&#xff09;一般包括分割操纵部分、识别失焦像素、分离阴影区域和检测隐藏对象。虽…

2024开源AI人脸视频替换最新版一张图实现视频或者图片换脸,完全免费自媒体!附演示

这是github上的 roop开源项目强大AI换脸模型&#xff0c;一个大哥的封装&#xff0c;可以拿去做自媒体但是注意别触碰法律 开源项目地址&#xff1a;https://github.com/s0md3v/roop Face Swap技术&#xff1a;开启面部特征融合新纪元 你是否想过&#xff0c;通过技术手段将…

【JavaEE】_第一个SpringBoot项目

目录 1. 第一个SpringBoot项目 1.1 创建项目 1.2 加载依赖 1.3 运行启动类 1.4 创建一个简单类试运行 2. 关于SpringBoot项目的目录结构 3. 关于修改文件名 4. 关于启动日志 5. 关于访问出错 5.1 404 5.1.2 URL输入错误 5.1.2 注解错误 5.2 500 5.3 无法访问此网…

Java进阶-反射

来学习一下Java的反射&#xff0c;通过Class实例获取class信息的方法称为反射&#xff08;Reflection&#xff09;&#xff0c;内容如下 一、反射机制 1、概述 在运行状态中&#xff0c;对于任意一个类&#xff0c;都能够知道这个类的所有属性和方法&#xff1b;对于任意一…

Linux基本指令(上)

在Linux中&#xff0c;将文件夹称为目录&#xff0c;后面的内容都与目录相关。 1. ls指令 语法&#xff1a; ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0c;将列出文件名以及其他信息。 常用选项 …

【精选】Java项目介绍和界面搭建——拼图小游戏 中

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

经销商文件分发 怎样兼顾安全和效率?

经销商文件分发是指将文件、资料、产品信息等从制造商或经销商传递给经销商的过程。这一过程对于确保经销商能够获取最新的产品信息、销售策略、市场活动资料等至关重要。 想要管理众多经销商合作伙伴之间的文件传输并提高效率&#xff0c;可以采取以下措施&#xff1a; 1、建…

容易发生内存泄漏的八个场景,你都知道吗?

内存泄漏与内存溢出 JVM在运行时会存在大量的对象&#xff0c;一部分对象是长久使用的&#xff0c;一部分对象只会短暂使用 JVM会通过可达性分析算法和一些条件判断对象是否再使用&#xff0c;当对象不再使用时&#xff0c;通过GC将这些对象进行回收&#xff0c;避免资源被用…

调试工具vue,react,redux

React Developer Tools Redux DevTools Vue devtools 使用浏览器官方组件扩展搜索安装

2369. 检查数组是否存在有效划分(动态规划)

2024-3-1 文章目录 [2369. 检查数组是否存在有效划分](https://leetcode.cn/problems/check-if-there-is-a-valid-partition-for-the-array/)思路&#xff1a;代码&#xff1a; 2369. 检查数组是否存在有效划分 思路&#xff1a; 1.状态定义:f[i]代表考虑将[0,i]是否能被有效划…

PMP考试培训费用多少钱?

PMP考试的相关费用包括报名费用、培训费用和证书续证费用三个部分。 一、PMP考试报名费用&#xff1a; 首次报考费用为3900元&#xff0c;如果未通过考试可以在英文报名有效期内进行补考报名&#xff0c;补考费用为2500元。 付费方式是在项目管理学会官方网站上提交报考资料…

ASO 对App产品性能的影响

可发现性 拥有出色的App还不足以让人们发现它&#xff0c;ASO技术通过提高搜索排名来增强App的可发现性。当用户可以在搜索结果中轻松找到应用程序时&#xff0c;那么下载和成功的潜力就会飙升。 设定期望 实施有效的应用商店优化不仅可以为潜在用户建立正确的期望&#xf…

职场的过早优化

过早优化&#xff0c;指的是还没弄清楚需求未来的变化的走向的时候&#xff0c;忽略了更重要的问题。 放在职业发展上&#xff1a;你在没有积累足够职场资源&#xff08;眼界、能力、人脉等等&#xff09;&#xff0c;也没有对职业发展形成清晰认知的时候&#xff0c;就过早地进…

Hive SQL 开发指南(二)使用(DDL、DML,DQL)

在大数据领域&#xff0c;Hive SQL 是一种常用的查询语言&#xff0c;用于在 Hadoop上进行数据分析和处理。为了确保代码的可读性、维护性和性能&#xff0c;制定一套规范化的 Hive SQL 开发规范至关重要。本文将介绍 Hive SQL 的基础知识&#xff0c;并提供一些规范化的开发指…

2023年世界前沿科技发展态势及2024年趋势展望——综述篇

源自&#xff1a;全球技术地图 “人工智能技术与咨询” 发布 一、2023年前沿科技发展与竞争态势总结 &#xff08;一&#xff09;人工智能引领前沿技术加速进步&#xff0c;产业应用一触即发&#xff0c;技术奇点苗头出现 &#xff08;二&#xff09;科技之变引发战争形态之…