Python Web开发记录 Day12:Django part6 用户登录

名人说:东边日出西边雨,道是无晴却有晴。——刘禹锡《竹枝词》
创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)

目录

      • 1、登录界面
      • 2、用户名密码校验
      • 3、cookie与session配置
        • ①cookie与session
        • ②配置
      • 4、登录验证
      • 5、注销登录
      • 6、图片验证码
        • ①Pillow库
        • ②图片验证码的实现
      • 7、补充:图片验证码的作用和扩展
        • ①作用
        • ②其他类型的验证码
      • 8、验证码校验

在上一篇博客中我们实现了管理员管理的内容,本篇博客则来实现用户登录,使系统的功能更加完善。

1、登录界面

1.在urls.py中添加用户列表的路径login/,并告诉该路径指向的视图account.login

from django.urls import path
from api.views import depart, user, pretty, admin, accounturlpatterns = [# 部门管理path("depart/list/", depart.depart_list),path("depart/add/", depart.depart_add),path("depart/delete/", depart.depart_delete),path("depart/<int:nid>/edit/", depart.depart_edit),# 用户管理path("user/list/", user.user_list),path("user/add/", user.user_add),path("user/model/form/add/", user.user_model_form_add),path('user/<int:nid>/edit/', user.user_edit),path("user/<int:nid>/delete/", user.user_delete),# 靓号管理path("pretty/list/", pretty.pretty_list),path("pretty/add/", pretty.pretty_add),path("pretty/<int:nid>/edit/", pretty.pretty_edit),path("pretty/<int:nid>/delete/", pretty.pretty_delete),# 管理员管理path('admin/list/', admin.admin_list),path('admin/add/', admin.admin_add),path('admin/<int:nid>/edit/', admin.admin_edit),path('admin/<int:nid>/delete/', admin.admin_delete),path('admin/<int:nid>/reset/', admin.admin_reset),# 用户登录path('login/', account.login),
]

2.在views文件夹下新建account.py,并在account.py中写出对应的函数,发出请求,并返回响应login.html

"""
from django.shortcuts import render
这段代码是导入Django中的一个函数`render`,用于渲染HTML模板并返回一个HttpResponse对象。
render函数通常在视图函数中使用,将数据和模板作为参数传递给它,然后它会根据***模板和数据生成HTML页面***,并返回给浏览器。
"""
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from api.utils.bootstrap import BootStrapForm
from api.utils.encrypt import md5
from api.models import Admin
from api.utils.code import check_code
from io import BytesIO# 这一次采用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,)code = forms.CharField(label="验证码",widget=forms.TextInput,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)# 验证码的校验user_input_code = form.cleaned_data.pop('code')image_code = request.session.get('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}request.session.set_expiry(60 * 60 * 24 * 7)return redirect("/admin/list/")return render(request, 'login.html', {"form": form})

3.创建templates目录下模版html文login.html,以此呈现做用户登录的界面。

模板layout.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><!--Bootstrap框架--><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}"><!--datetimepicker插件--><link rel="stylesheet" type="text/css"href="{% static 'plugins/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css' %}">{% block css %}<style>.navbar {border-radius: 0;}</style>{% 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="/pretty/list">靓号管理</a></li>{#                <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>#}</ul><ul class="nav navbar-nav navbar-right"><li><a href="/login">登录</a></li><li class="dropdown"><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><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><div><div class="container">{% block content %}{% endblock %}</div>
</div>{% block js %}<script src="{% static 'js/jquery.min.js' %}"></script><!-- 加载 Bootstrap DateTimePicker JS --><script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.js' %}"></script><script src="{% static 'plugins/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js' %}"></script><script src="{% static 'plugins/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js' %}"></script><script type="text/javascript">$(function () {//当容器加载完成,对容器调用工具函数$("#dt").datetimepicker({language: 'zh-CN', //语言format: 'yyyy-mm-dd',//日期的格式minView: 'month', //可以选择的最小视图initialDate: new Date(),//初始化显示的日期autoclose: true,//设置选择完日期或者时间之后,日否自动关闭日历todayBtn: true,//设置自动显示为今天clearBtn: false//设置是否清空按钮,默认为false});});</script>
{% endblock %}

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" onclick="this.setAttribute('src','/image/code/?random='+Math.random())"></div></div></div><button type="submit" class="btn btn-primary center-block" style="width: 80px;">登 录</button></form></div>
</div>
{% endblock %}

效果:

image-20240316094200228

2、用户名密码校验

在用户登录时,我们需要考虑一个问题,用户所输入的用户名和密码是否正确?是否与数据库中存储的用户名和密码一致?接下来我们就来解决这些问题。

1.修改login.html、form.py和account.py

a.修改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" onclick="this.setAttribute('src','/image/code/?random='+Math.random())"></div></div></div><button type="submit" class="btn btn-primary center-block" style="width: 80px;">登 录</button></form></div>
</div>
{% endblock %}

在templates目录下新建error.html

error.html

{% extends 'layout.html' %}{% block content %}<div class="container"><div class="alert alert-danger" role="alert">{{ msg }}</div></div>
{% endblock %}

b.在form.py新增用户登录模块,此次采用Form来实现。

class LoginForm(BootStrapForm):username = forms.CharField(label="用户名",widget=forms.TextInput(attrs={"class": "form-control"}),required=True)password = forms.CharField(label="密码",widget=forms.PasswordInput(attrs={"class": "form-control"}, render_value=True),required=True)code = forms.CharField(label="验证码",widget=forms.TextInput(attrs={"class": "form-control"}),required=True,)def clean_password(self):pwd = self.cleaned_data.get("password")return md5(pwd)

c.修改account.py

"""
from django.shortcuts import render
这段代码是导入Django中的一个函数`render`,用于渲染HTML模板并返回一个HttpResponse对象。
render函数通常在视图函数中使用,将数据和模板作为参数传递给它,然后它会根据***模板和数据生成HTML页面***,并返回给浏览器。
"""
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from api.utils.bootstrap import BootStrapForm
from api.utils.encrypt import md5
from api.models import Admin
from api.utils.code import check_code
from io import BytesIO# 这一次采用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,)code = forms.CharField(label="验证码",widget=forms.TextInput,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)# 验证码的校验user_input_code = form.cleaned_data.pop('code')image_code = request.session.get('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}request.session.set_expiry(60 * 60 * 24 * 7)return redirect("/admin/list/")return render(request, 'login.html', {"form": form})

效果:输入用户名或密码错误时,此时登录时会提示“用户名或密码错误”

image-20240316163829029

3、cookie与session配置

在解决了用户名密码校验后,又遇到了一个问题,那就是我们平时访问一些网站会提示是否接受cookies?它是什么?有什么用?我们项目中怎么引入来它呢?

①cookie与session

Cookie和Session都是用于维持用户状态和数据的机制,它们在Web应用中扮演着重要的角色。下面我将用中文详细解释Cookie和Session的概念,并使用Markdown进行格式化。

1.Cookie

Cookie是一种在客户端(通常是浏览器)存储数据的机制。它以键值对的形式存储在浏览器中,由服务器在HTTP响应头中设置,并在后续的请求中发送回服务器。

  • 特点

    • 存储在客户端浏览器中
    • 大小限制:一般不超过4KB
    • 可以设置过期时间
    • 可以设置访问路径和域名
    • 每次请求都会自动携带相应的Cookie
  • 用途

    • 会话管理:记录用户的登录状态、购物车等
    • 个性化设置:记录用户的偏好、主题等
    • 行为跟踪:记录用户的浏览行为、广告点击等

2.Session

Session是在服务器端存储用户状态和数据的机制。它基于Cookie实现,但数据存储在服务器端。当用户第一次访问网站时,服务器会生成一个唯一的Session ID,并将其以Cookie的形式发送给客户端。后续的请求中,客户端会将Session ID发送回服务器,服务器根据Session ID获取对应的用户数据。

  • 特点

    • 存储在服务器端
    • 大小限制:理论上没有限制,取决于服务器的内存大小
    • 安全性相对较高,数据不容易被篡改
    • 依赖Cookie传递Session ID
  • 用途

    • 用户认证:存储用户的登录状态
    • 敏感数据的存储:存储用户的个人信息、权限等
    • 服务器端数据共享:在不同的页面或请求之间共享数据

3.Cookie与Session的区别

特性CookieSession
存储位置客户端(浏览器)服务器端
大小限制一般不超过4KB理论上没有限制,取决于服务器的内存大小
安全性相对较低,数据容易被篡改相对较高,数据存储在服务器端
数据传递每次请求都会自动携带相应的Cookie依赖Cookie传递Session ID
用途会话管理、个性化设置、行为跟踪等用户认证、敏感数据存储、服务器端数据共享
②配置

1.修改account.py中的login函数

image-20240316164346245

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)# 验证码的校验user_input_code = form.cleaned_data.pop('code')image_code = request.session.get('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}request.session.set_expiry(60 * 60 * 24 * 7)return redirect("/admin/list/")return render(request, 'login.html', {"form": form})

在登录成功后,页面会跳转到管理员界面,而session信息将保存在服务端数据库django_session表中。

2.mysql显示表中数据

mysql> show tables;
+----------------------------+
| Tables_in_henan16          |
+----------------------------+
| api_admin                  |
| api_department             |
| api_prettynum              |
| api_userinfo               |
| 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             |
+----------------------------+
14 rows in set (0.00 sec)mysql> select * from django_session;
+----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+
| session_key                      | session_data                                                                                                                                                    | expire_date                |
+----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+
| z4ujk56tzhhareeh77z353vvm0upmaca | eyJpbWFnZV9jb2RlIjoiRklLUkYiLCJfc2Vzc2lvbl9leHBpcnkiOjYwfQ:1rlAeT:qgyi412exxDZPTwag0HYh7jtGUyc-Kav-VvQ_2MxsaI                                                   | 2024-03-15 16:44:25.482192 |
+----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------+----------------------------+
1 rows in set (0.01 sec)

image-20240315234226850

4、登录验证

在实现了这个用户名密码验证之后,我们要对每个页面进行验证,只有登录的人才能访问这些页面,怎么实现登录验证?这个问题我们学了中间件后就会变得越发清晰。

中间件会在视图函数下的每个方法执行前调用,不用再每个方法下进行判断,不然会导致函数过多显得冗杂。

1.新建middleware目录,并在该目录下新建auth.py文件

image-20240316165701978

auth.py

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirectclass AuthMiddleware(MiddlewareMixin):def process_request(self, request):# 0.排除那些不需要登录就能访问的页面#   request.path_info 获取当前用户请求的URL /login/if request.path_info in ["/login/", "/image/code/"]:return# 1.读取当前访问的用户的session信息,如果能读到,说明已登陆过,就可以继续向后走。info_dict = request.session.get("info")# print(info_dict)if info_dict:return# 2.没有登录过,重新回到登录页面return redirect('/login/')

2.修改settings.py

image-20240316165804072

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",'api.middleware.auth.AuthMiddleware',
]

到了这里,你会发现,只要你没登陆过,你无论访问哪个板块的页面,都会跳转到登录界面。

那么此时登录校验的功能就实现了,但是由于目前无法退出,说明该用户登录功能仍有缺陷需要改善,接下来咱们就对这一部分做进一步的介绍。

5、注销登录

1.在layout.html中配置注销(退出登录的)的跳转URL路径。

image-20240315235913270

layout.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><!--Bootstrap框架--><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><link rel="stylesheet" href="{% static 'plugins/font-awesome-4.7.0/css/font-awesome.css' %}"><!--datetimepicker插件--><link rel="stylesheet" type="text/css"href="{% static 'plugins/bootstrap-datetimepicker/css/bootstrap-datetimepicker.css' %}">{% block css %}<style>.navbar {border-radius: 0;}</style>{% 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="/pretty/list">靓号管理</a></li>{#                <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>#}</ul><ul class="nav navbar-nav navbar-right"><li><a href="/login">登录</a></li><li class="dropdown"><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><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><div><div class="container">{% block content %}{% endblock %}</div>
</div>{% block js %}<script src="{% static 'js/jquery.min.js' %}"></script><!-- 加载 Bootstrap DateTimePicker JS --><script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.js' %}"></script><script src="{% static 'plugins/bootstrap-datetimepicker/js/bootstrap-datetimepicker.min.js' %}"></script><script src="{% static 'plugins/bootstrap-datetimepicker/js/locales/bootstrap-datetimepicker.zh-CN.js' %}"></script><script type="text/javascript">$(function () {//当容器加载完成,对容器调用工具函数$("#dt").datetimepicker({language: 'zh-CN', //语言format: 'yyyy-mm-dd',//日期的格式minView: 'month', //可以选择的最小视图initialDate: new Date(),//初始化显示的日期autoclose: true,//设置选择完日期或者时间之后,日否自动关闭日历todayBtn: true,//设置自动显示为今天clearBtn: false//设置是否清空按钮,默认为false});});</script>
{% endblock %}

2.在urls.py中添加用户列表的路径logout/,并告诉该路径指向的视图view.logout

urls.py

from django.urls import path
from api.views import depart, user, pretty, admin, accounturlpatterns = [# 部门管理path("depart/list/", depart.depart_list),path("depart/add/", depart.depart_add),path("depart/delete/", depart.depart_delete),path("depart/<int:nid>/edit/", depart.depart_edit),# 用户管理path("user/list/", user.user_list),path("user/add/", user.user_add),path("user/model/form/add/", user.user_model_form_add),path('user/<int:nid>/edit/', user.user_edit),path("user/<int:nid>/delete/", user.user_delete),# 靓号管理path("pretty/list/", pretty.pretty_list),path("pretty/add/", pretty.pretty_add),path("pretty/<int:nid>/edit/", pretty.pretty_edit),path("pretty/<int:nid>/delete/", pretty.pretty_delete),# 管理员管理path('admin/list/', admin.admin_list),path('admin/add/', admin.admin_add),path('admin/<int:nid>/edit/', admin.admin_edit),path('admin/<int:nid>/delete/', admin.admin_delete),path('admin/<int:nid>/reset/', admin.admin_reset),# 用户登录path('login/', account.login),path('logout/', account.logout),
]

3.在account.py中新增logout函数,用于实现用户注销登录功能。

account.py

def logout(request):"""用户注销"""# 清除sessionrequest.session.clear()return redirect("/login/")

效果:

129

6、图片验证码

为了使用户登录功能更加完善,加入图片验证码的功能,上面的内容大家已经可以看到图片验证码,并且代码已经在上面体现了,那接下来重要讲解一下图片验证码的实现思路。

在此之前我们先要认识一个库pillow:

①Pillow库

Pillow是一个Python图像处理库,它是Python Imaging Library (PIL) 的一个分支。Pillow提供了丰富的图像处理功能,可以进行图像的打开、保存、裁剪、调整大小、旋转、滤镜应用、颜色转换等操作。它支持多种常见的图像格式,包括JPEG、PNG、GIF、BMP等。

Pillow库易于安装和使用,并且在处理图像时提供了简洁而直观的API。它广泛应用于Web开发、数据分析、图像处理等领域,可以用来处理图像、生成缩略图、添加水印、图像识别等任务。

1.下载pillow

pip install pillow

2.验证码字体下载

验证码字体下载链接,点击此处下载

3.下载完之后,解压,并在utils目录下新建ttf目录,将字体文件复制到该目录下。

image-20240316002051306

效果:

image-20240316003141901

②图片验证码的实现

1.在urls.py中添加用户列表的路径image/code/,并告诉该路径指向的视图account.image_code

from django.urls import path
from api.views import depart, user, pretty, admin, accounturlpatterns = [# 部门管理path("depart/list/", depart.depart_list),path("depart/add/", depart.depart_add),path("depart/delete/", depart.depart_delete),path("depart/<int:nid>/edit/", depart.depart_edit),# 用户管理path("user/list/", user.user_list),path("user/add/", user.user_add),path("user/model/form/add/", user.user_model_form_add),path('user/<int:nid>/edit/', user.user_edit),path("user/<int:nid>/delete/", user.user_delete),# 靓号管理path("pretty/list/", pretty.pretty_list),path("pretty/add/", pretty.pretty_add),path("pretty/<int:nid>/edit/", pretty.pretty_edit),path("pretty/<int:nid>/delete/", pretty.pretty_delete),# 管理员管理path('admin/list/', admin.admin_list),path('admin/add/', admin.admin_add),path('admin/<int:nid>/edit/', admin.admin_edit),path('admin/<int:nid>/delete/', admin.admin_delete),path('admin/<int:nid>/reset/', admin.admin_reset),# 用户登录path('login/', account.login),path('logout/', account.logout),path('image/code/', account.image_code),
]

2.在utils目录下新建code.py文件,该文件用于生成验证码。

code.py

from PIL import Image, ImageDraw, ImageFilter, ImageFont
import randomdef check_code(width=120, height=30, char_length=5, font_file='api/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')

3.修改account.py,在其中添加image_code函数,并导入code.py,以实现图片验证码的生成。

account.py

def image_code(request):"""图片验证码"""# 调用pillow函数,生成图片img, code_string = check_code()# 写入自己的sessionrequest.session['image_code'] = code_string# 设置60s超时request.session.set_expiry(60)# 将图片保存到内存里stream = BytesIO()img.save(stream, 'png')return HttpResponse(stream.getvalue())

效果:

image-20240316172949050

7、补充:图片验证码的作用和扩展

①作用

在用户登录时增加图片验证码的主要目的是提高网站的安全性,防止一些自动化程序或者恶意攻击。具体如下:

  • 防止暴力破解

  • 防止大量无效请求

  • 防止自动注册和垃圾评论

  • 提高用户体验

总的来说,图片验证码是一种简单而有效的安全措施,可以防止一些常见的攻击方式,提高网站的安全性和用户体验。虽然它并不是万能的,但它可以与其他安全措施(如密码复杂度要求、多因素身份验证等)结合使用,构建一个更加安全的登录系统

②其他类型的验证码
  1. 文本验证码:通过纯文本的形式出现,通常是一些简单的问题或者算术题,用户需要填写正确的答案。

  2. 滑动验证码:用户需要按照提示拖动滑块到指定位置,通过人机交互的方式完成验证。

  3. 短信验证码:网站向用户的手机发送一个随机的验证码,用户需要将收到的验证码填写到网站上进行验证。

  4. 行为验证码:通过分析用户的行为模式(如鼠标轨迹、键盘输入速度等)来判断。

在实现了图片验证码后,我们还要实现验证码的校验,即判断用户输入的验证码与生成的验证码是否一样,增加了验证码校验之后,网站的安全性也会得到一定的提升。

8、验证码校验

到了这里我们的图片验证码和验证码校验都完成了,但是还可以做一个小的优化,就是点击图片刷新验证码,我们只需要在login.html文件中修改img行就可以。

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

怎么理解这行代码?

这行代码其实是一个HTML的<img>标签,它用于显示图像

  • src="/image/code/":指定了图像的来源路径为"/image/code/",这里可能是一个服务器上的图像文件或者一个处理图像验证码的URL。
  • alt="":指定了图像的替代文本,当图像无法加载时会显示该文本。
  • id="image_code":为图像元素指定了一个唯一的标识符,可以在JavaScript中使用该标识符来操作该元素。
  • onclick="this.setAttribute('src','/image/code/?random='+Math.random())":定义了一个点击事件处理函数,当图像被点击时,会执行这个函数。函数的作用是将图像的src属性设置为"/image/code/?random="加上一个随机数,这样可以强制浏览器重新加载图像,实现刷新验证码的效果。

总的来说,这行代码是在HTML中创建了一个图像元素,并定义了点击事件处理函数,用于刷新图像验证码。当图像被点击时,会通过设置src属性为一个新的URL来重新加载图像。

image-20240316004420420

最后整体效果:

128

在完成了部门管理、用户管理、靓号管理、管理员管理、用户登录等之后,我们这一部分的内容就暂时告一段落,接下来会学习ajax的内容,我们为什么要学习ajax,因为我们实现网页内容的动态更新和无刷新的用户交互可以用到它,它通过在后台与服务器进行数据交换,使网页能够在不刷新整个页面的情况下更新部分内容,这样系统的交互性会更好

很感谢你能看到这里,如有相关疑问,还请下方评论留言。
Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊)
希望本篇内容能对大家有所帮助,如果大家喜欢的话,请动动手点个赞和关注吧,非常感谢你们的支持!

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

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

相关文章

STM32CubeMX与HAL库开发教程八(串口应用/轮询/中断/DMA/不定长数据收发)

目录 前言 初识串口-轮询模式 串口中断模式收发 串口DMA模式 蓝牙模块与数据包解析 前言 前面我们简单介绍过串口的原理和初步的使用方式&#xff0c;例如怎么配置和简单的收发&#xff0c;同时我们对串口有了一个初步的了解&#xff0c;这里我们来深入的来使用一下串口 …

Linux服务器(RedHat、CentOS系)安全相关巡检shell脚本

提示&#xff1a;巡检脚本可以使用crontab定时执行&#xff0c;人工根据执行结束时间点统一收集报告文件即可。 #!/bin/bash# Define output file current_date$(date "%Y%m%d") # Gets the current date in YYYYMMDD format echo >server_security_inspection_r…

【计算机网络】https的工作原理以及和http的区别

目录 前言 1. HTTP协议存在的问题 2. 什么是HTTPS协议&#xff1f; 3. HTTP和HTTPS有哪些区别&#xff1f; 4. HTTPS的工作原理 加密方式 前言 在日常的Web项目练习中&#xff0c;我们会发现老师会让我们在打开服务器之后使用 http://localhost/...进行项目效果测试和预览…

2.26OS分类,中断(内,外),系统调用,操作系统结构、引导,虚拟机(两类VMM),进程

外核可以申请分配连续的磁盘块以支持频繁的随机访问&#xff0c;其它的方式是采用虚拟存储 分层结构

iPhone 的健康数据采用的是 FHIR 传输格式

虽然感觉 FHIR 的数据传输格式还是有点繁琐的&#xff0c;但貌似现在也是唯一的事实上的标准。 通过 iPhone 健康上面查看的数据来看&#xff0c;有关健康的数据还是使用 FHIR 的数据传输格式。 不管怎么样&#xff0c;针对老旧的数据传输格式来看&#xff0c;FHIR 至少目前还是…

实现HBase表和RDB表的转化(附Java源码资源)

实现HBase表和RDB表的转化 一、引入 转化为HBase表的三大来源&#xff1a;RDB Table、Client API、Files 如何构造通用性的代码模板实现向HBase表的转换&#xff0c;是一个值得考虑的问题。这篇文章着重讲解RDB表向HBase表的转换。 首先&#xff0c;我们需要分别构造rdb和hba…

学习开发小程序的起航日记

2024年3月16日 不知不觉中三月份还只剩了一半的光景&#xff0c;我想写的内容还很多没有写&#xff0c;或者更应该说&#xff0c;是想积累的还有很多。现在最应该去完善Java的内容&#xff0c;可还是想先等等。想等搞清楚小程序部分&#xff0c;想等积累完小程序的内容。 这几…

网络安全——关于防火墙

网络安全防火墙是很重要的部分&#xff0c;关于防火墙我们要知道&#xff0c;他默认所有流量都是黑名单&#xff0c;只有开启允许通过才可以。 我们通过一个实验来学防火墙命令。 防火墙要登录才能使用&#xff0c;用户名是admin,默认密码是Admin123&#xff0c;在第一次登录…

Visual Studio配置libtorch(cuda安装一步到位)

Visual Studio配置libtorch visual Studio安装cuDNN安装CUDAToolkit安装libtorch下载Visual Studio配置libtorch(cuda版本配置) visual Studio安装 visual Studio点击安装 具体的安装和配置过程这里就不进行细讲了&#xff0c;可以参考我这篇博客Visual Studio配置OpenCV(保姆…

Java关于物联网消息引擎:EMQ X

1.背景 1、5G 时代&#xff0c;万物互联 随着5G的到来&#xff0c;万物互联已经成为现实&#xff0c;物联网行业得以蓬勃发展&#xff0c;催生了很多的应用&#xff0c;比如&#xff1a;物联网pass平台&#xff0c;车联网&#xff0c;面向云平台的IOT-Hub&#xff0c;NB-IoT蜂…

Simulink|局部遮荫下光伏组件多峰值PSO-MPPT控制

目录 主要内容 1.光伏电池工程数学模型的输出特性程序 2.普通扰动观察法进行MPPT 3.基于粒子群寻优的多峰输出特性 4.PSO_MPPT仿真模型 程序下载链接 主要内容 在实际的光伏发电系统中,由于环境多变等因素的影响,当局部出现被遮挡情况时光伏阵列的功率-电压(P-U)特…

【C语言】字符与字符串---从入门到入土级详解

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.字符类型和字符数组&#xff08;串&#xff09;简介 1.ASCII 2.定义&#xff0c;初始化&#xff0c;使用 1>字符的定义及初始化 2>字符串的定义及初始化 二.…

flink1.18.0 自定义函数 接收row类型的参数

比如sql中某字段类型 array<row<f1 string,f2 string,f3 string,f4 bigint>> 现在需要编写 tableFunction 需要接受的参数如上 解决方案 用户定义函数|阿帕奇弗林克 --- User-defined Functions | Apache Flink

传输层/UDP/TCP协议

再谈端口号 TCP/IP协议用“源IP”&#xff0c;“源端口号”&#xff0c;“目的IP”&#xff0c;“目的端口号”&#xff0c;“协议号”&#xff0c;这样一个五元组来标识一个通信&#xff08;可以用netstat -n来查看&#xff09;。 端口号的划分和知名端口号 我们之前就说过&am…

Jenkins通知目标服务器拉取Harbor镜像部署

1.告诉目标服务器拉取哪个镜像 2.判断当前有没有正在运行此容器&#xff0c;有就删除 3.接着查看拉取的镜像目标服务器上是否已存在&#xff0c;有就删除 4.拉取Harbor镜像 5.运行容器 目标服务器编写脚本 创建个部署脚本 vim deploy.sh告诉目标服务器Harbor地址、仓库、镜像…

【SQL】1193. 每月交易 I 【年月日(日期)拼接相关函数】

前述 知识点学习&#xff1a; SQL 日期函数 day() 、month()、year() 各种使用方法mysql 两个字符年月拼接 题目描述 leetcode题目&#xff1a;1193. 每月交易 I 思路 先按照年月排&#xff0c;再按照country排列 日期拼接相关的函数 year(): 截取年份&#xff1b;month…

Java设计模式 | 设计模式概述和分类

独孤求败五重境界 利剑&#xff08;“凌厉刚猛&#xff0c;无坚不摧&#xff0c;弱冠前以之与河朔群雄争锋。”&#xff09;软剑&#xff08;“紫薇软剑&#xff0c;三十岁前所用&#xff0c;误伤义士不祥&#xff0c;乃弃之深谷。”&#xff09;重剑&#xff08;“重剑无锋&a…

宜搭faas服务器获取accessToken

可以用faas服务器的OpenAPIUtil.getCustomAccessTokenThenCache&#xff08;Client ID,Client Secret&#xff09;就可以获取 至于获取这个Client ID&#xff0c;Client Secret 就需要在钉钉开放平台创建一个应用 然后在这个应用的基础信息里面有 注意的是&#xff1a;如果需要…

「SpringBrick快速入门指南」:一款基于Spring Boot的高级插件化开发框架

文章目录 关于 | About技术文档 | Document开源项目 | Project 案例 | Demo项目结构 | Structure主程序配置集成 | Settings引入框架依赖 | Framework在配置文件加入配置 | YamlSpringBoot启动类改引导类 | Change 插件配置集成 | Settings引入依赖 | XML定义插件引导类 | Clas…

JUnit 面试题及答案整理,最新面试题

JUnit中的断言&#xff08;Assert&#xff09;有哪些类型&#xff1f; JUnit提供了多种断言类型来帮助测试代码的正确性。常见的断言类型包括&#xff1a; 1、assertEquals&#xff1a; 用于检查两个值是否相等。如果不相等&#xff0c;测试失败。 2、assertTrue和assertFal…