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

news2025/4/8 15:43:14

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})

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

前端AR图像增强 + 图像追踪 + 模型渲染

文章目录 背景介绍技术介绍准备目标图片准备3D模型整合到一起演示代码地址背景介绍 本文实现web端html实现AR识别功能 在日常生活中常常看到AR虚拟现实相结合的案例 如下图的效果匹配到目标图片后展示3D模型 从而提高真实度 AR识别 技术介绍 想要达到效果有以下几步是必须的 准…

https://htmlunit.sourceforge.io/

https://htmlunit.sourceforge.io/ 爬虫 HtmlUnit – Welcome to HtmlUnit HtmlUnit 3.11.0 API https://mvnrepository.com/artifact/net.sourceforge.htmlunit/htmlunit/2.70.0 https://s01.oss.sonatype.org/service/local/repositories/releases/content/org/htmlunit…

西软云XMS operate XXE漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

【Java程序员面试专栏 数据结构】一 高频面试算法题:数组

一轮的算法训练完成后,对相关的题目有了一个初步理解了,接下来进行专题训练,以下这些题目就是汇总的高频题目,本篇主要聊聊数组,包括数组合并,滑动窗口解决最长无重复子数组问题,图形法解下一个排列问题,以及一些常见的二维矩阵问题,所以放到一篇Blog中集中练习 题目…

ChatGpt 使用fetch-event-source实现sse流式处理

microsoft/fetch-event-source 是一个由微软提供的库&#xff0c;用于在客户端和服务器之间建立基于 EventSource 的连接。EventSource 是一种 HTTP 协议&#xff0c;允许服务器向客户端推送实时事件流。该库提供了对 EventSource 协议的封装&#xff0c;使得在前端 JavaScript…

CCF-A类 IEEE VIS‘24 3月31日截稿!探索可视化技术的无限可能!

会议之眼 快讯 IEEE VIS (IEEE Visualization Conference )即可视化大会将于 2024 年 10月13日 -18日在美国佛罗里达州皮特海滩的信风岛大海滩度假举行&#xff01;圣彼得海滩&#xff0c;以其迷人的日落和和煦的微风&#xff0c;作为激发创造力和促进可视化社区内合作的完美背…

数据结构与算法之美学习笔记:55 | 算法实战(四):剖析微服务接口鉴权限流背后的数据结构和算法

目录 前言鉴权背景介绍如何实现快速鉴权&#xff1f;限流背景介绍如何实现精准限流&#xff1f;总结引申 前言 本节课程思维导图&#xff1a; 微服务是最近几年才兴起的概念。简单点讲&#xff0c;就是把复杂的大应用&#xff0c;解耦拆分成几个小的应用。这样做的好处有很多。…

外汇天眼:Sumsub推出播客,讨论最新的欺诈威胁

Sumsub&#xff0c;一家全球验证平台&#xff0c;今天宣布推出自己的播客&#xff0c;名为《什么是欺诈&#xff1f;》。节目将邀请来自各行各业的专业嘉宾&#xff0c;包括人工智能、网络安全、金融科技、加密货币和互联网游戏等领域的专家。对话将集中讨论数字欺诈如何影响企…

服了,阿里云服务器价格和腾讯云1元之差,如何选择?

2024年阿里云服务器和腾讯云服务器价格战已经打响&#xff0c;阿里云服务器优惠61元一年起&#xff0c;腾讯云服务器62元一年&#xff0c;2核2G3M、2核4G、4核8G、8核16G、16核32G、16核64G等配置价格对比&#xff0c;阿腾云atengyun.com整理阿里云和腾讯云服务器详细配置价格表…

Apache Paimon Append Queue表解析

a) 定义 在此模式下&#xff0c;将append table视为由bucket分隔的queue。 同一bucket中的每条record都是严格排序的&#xff0c;流式读取将完全按照写入顺序将record传输到下游。 使用此模式&#xff0c;无需特殊配置&#xff0c;所有数据都将作为queue进入一个bucket&…

XSS中级漏洞(靶场)

目录 一、环境 二、正式开始闯关 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x0B 0x0C 0x0D 0x0E ​ 0x0F 0x10 0x11 0x12 一、环境 在线环境&#xff08;gethub上面的&#xff09; alert(1) 二、正式开始闯关 0x01 源码&#xff1a; 思路&#xff1a;闭…

【vue3学习笔记】Suspense组件;vue3中的其它改变

尚硅谷Vue2.0Vue3.0全套教程丨vuejs从入门到精通 课程 P167节 《Suspense组件》笔记 想要学习suspense&#xff0c;先来了解静态组件与异步组件。 静态引入与异步引入&#xff1a; 在network中将网速调慢&#xff0c;观察在静态引入和异步引入模式下&#xff0c;两个组件的加载…

Programming Abstractions in C阅读笔记:p306-p307

《Programming Abstractions in C》学习第75天&#xff0c;p306-p307总结&#xff0c;总计2页。 一、技术总结 1.Quicksort algorithm(快速排序) 由法国计算机科学家C.A.R(Charles Antony Richard) Hoare&#xff08;东尼.霍尔&#xff09;在1959年开发(develop), 1961年发表…

力扣每日一题 使二叉树所有路径值相等的最小代价 满二叉树 贪心

Problem: 2673. 使二叉树所有路径值相等的最小代价 文章目录 思路复杂度Code 思路 &#x1f468;‍&#x1f3eb; 灵神题解 复杂度 ⏰ 时间复杂度: O ( n ) O(n) O(n) &#x1f30e; 空间复杂度: O ( 1 ) O(1) O(1) Code class Solution {public int minIncrements(int …

瑞_23种设计模式_外观模式

文章目录 1 外观模式&#xff08;Facade Pattern&#xff09;1.1 介绍1.2 概述1.3 外观模式的结构 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 jdk源码解析 &#x1f64a; 前言&#xff1a;本文章为瑞_系列专栏之《23种设计模式》的外观模式篇。本文中的部分…

一篇关于,搬运机器人的介绍

搬运机器人是一种能够自动运输和搬运物品的机器人。它们通常配备有传感器和导航系统&#xff0c;可以在工厂、仓库、医院或其他场所自主移动&#xff0c;并且可以根据预先设定的路径或指令进行操作。 搬运机器人可以用于搬运重物、物料搬运、装卸货物、仓库管理等任务。它们可以…

Git自动忽略dll文件的问题

检查了半天发现是sourcetreee的全局忽略文件导致&#xff0c; 从里面删除dll即可。 我是干脆直接删了全局忽略&#xff0c;太恶心了&#xff0c;如下&#xff1a; #ignore thumbnails created by windows Thumbs.db #Ignore files build by Visual Studio *.exe .vsconfig .s…

如何在Windows部署TortoiseSVN客户端并实现公网连接内网VisualSVN服务端

文章目录 前言1. TortoiseSVN 客户端下载安装2. 创建检出文件夹3. 创建与提交文件4. 公网访问测试 前言 TortoiseSVN是一个开源的版本控制系统&#xff0c;它与Apache Subversion&#xff08;SVN&#xff09;集成在一起&#xff0c;提供了一个用户友好的界面&#xff0c;方便用…

USACO 2024 Feb Bronze铜组题解

闲话:今天是4年一度的奇观——2月29日!(地球人都知道) 所以为了纪念这个特殊的日子&#xff0c;我决定倒着讲。这是什么奇怪的规矩&#xff1f;(雾 Maximizing Productivity: 二分即可。 #include <bits/stdc.h> using namespace std; const int maxn200005; int c[ma…

市场复盘总结 20240229

仅用于记录当天的市场情况&#xff0c;用于统计交易策略的适用情况&#xff0c;以便程序回测 短线核心&#xff1a;不参与任何级别的调整&#xff0c;采用龙空龙模式 一支股票 10%的时候可以操作&#xff0c; 90%的时间适合空仓等待 二进三&#xff1a; 进级率中 60% 最常用…