Hello 大家新年好。今天这篇博客是用来填补之前的登录系统的不足所遗留下来的坑点,你们知道的,我有坑是必补啊。
首先我留的第一个坑点不知道大家有没有注意到,当我们没并没有登录的时候,但是如果我们事先知道一些内部测试的网站路由的话,我们可以跳过登录直接进入到里面。
我们可以发现,明明我并没有进行登录,但是如果有人知道我的路由,它就可以直接进来,这肯定是不允许的,因此我要引用一个新的内容-------session与cookie
Cookie是由服务器发送给浏览器的小型文本文件,存储在用户的计算机上。它包含有关用户的信息,如用户ID、首选项和购物车内容。当用户访问网站时,浏览器会将Cookie发送回服务器,以便服务器可以根据存储在Cookie中的信息来识别用户并提供个性化的服务。
Session是服务器上存储用户会话数据的一种机制。当用户访问网站时,服务器会为每个会话创建一个唯一的ID,并将该ID存储在Cookie中或通过URL参数传递给浏览器。然后,服务器使用该ID来跟踪用户的会话数据,如登录状态、购物车内容等。与Cookie不同,Session数据保存在服务器上,而不是用户的计算机上。
一般来讲,网站都是通过记录你的session和cookie来判断你是否登录过,如果网站记录过你存在的session和cookie记录,那么网站会让你直接进入而不需要再次登录,我们可以利用这个特点来进行判断当前是否存在该用户,如果没有则让其返回登录界面。
login.py
def login(request):
if request.method == 'GET':
form = LoginForm()
return render(request,'login.html',{'form':form})
form = LoginForm(data=request.POST)
if form.is_valid():
# 验证码验证
user_input_code = form.cleaned_data.pop('code')
# 没有值设置为空
code = request.session.get('image_code','')
# 对于验证码不区分大小写
if user_input_code.upper() != code.upper():
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:
# print(form.cleaned_data)
form.add_error('password','用户名或者密码错误')
return render(request,'login.html',{'form':form})
# 生成字符串存储到cookie和session当中去
info = {
'id':admin_object.id,
'name':admin_object.username,
'role':admin_object.role
}
request.session['info'] = info
# 时效性 ------ 一个点
request.session.set_expiry(60 * 60 * 24)
# request.session.set_expiry(10)
return redirect('/')
return render(request,'login.html',{'form':form})
我们通过记录用户登录的数据来作为用户的session数据(为什么使用这个后面会有解释),然后对我们记录的session加一个时效性进行约束,过了这个时间session自动删除。
现在我们开始写对于如何判断是否存在该用户,首先我先介绍一个苯的方法,随便找一个模块方法,例如部门列表:
depart_list:
对于每一个模块我们都在里面进行一个对session进行一个判断,如果不存在就重定向到登录界面,但是这样的缺点就是会让我们的代码看上去非常的冗余且不容易维护,要是我们有100个这样的模块方法,那这样写我们就得写100个,当然我们肯定不会用这样的方法进行编写,那有没有办法让计算机自动为我们进行判断是否存在session的方法呢? 接下来我来介绍一种新的方式----中间件的使用。
中间件(Middleware)是位于Web应用程序的请求和响应处理管道中的组件。它负责在处理请求之前和处理响应之后执行特定的任务。中间件可以用于许多不同的目的,如身份验证、日志记录、错误处理等。
中间件的特点和优势包括:
可重用性:中间件可以在多个不同的请求和应用程序中重复使用,提高了代码的可重用性。
可插拔性:中间件可以灵活地添加和移除,以满足不同的需求。这使得应用程序可以根据需要选择和配置适当的中间件。
解耦性:中间件可以将不同的功能和任务解耦,使得代码更加模块化和可维护。
可扩展性:由于中间件可以按照自定义的顺序链式调用,因此可以很容易地实现功能的扩展和组合。
由于中间件具有可重用性这一优良特点,我们可以在中间件中对于session进行一个判断,然后将我们需要进行判断的模块放入到其中。
创建一个新的文件夹为middleware(不能使用别的名称,不然得去配置修改),在其中创建一个auth模块用于检验。
在其中创建一个中间件类,导入相关模块
# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class AuthMiddleware(MiddlewareMixin):
def process_request(self,request):
print('这是入口')
def process_response(self,request,response):
print('这是出口')
然后进入setting.py中,添加创建的中间件模块
各位读者可以自行测试一下这个中间件,随便进入一个模块,然后看一下控制台输入了什么,就可以明白其工作原理啦。
# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class AuthMiddleware(MiddlewareMixin):
def process_request(self,request):
if request.path_info in ['/login/','/logout/','/image/code/']:
return
return redirect('/login/')
def process_response(self,request,response):
return response
为什么要对登录和退出以及验证码进行一个额外的判断,因为如果你不能加入这个的话,如果此时你并没有登录,那么你就会一直对于登录重定向,始终也不会到达登录界面,这个大家自行检验,如果把这个if删掉会发生什么。
现在我们开始最开始的操作,如果我在不登录的前提下,我通过路由进入部门列表会发生什么。
我们可以通过验证码注意到,我们其实又回到了登录界面,刷新了一下界面也就是进行了一个重定向操作。
现在我们输入账号和密码进入到我们的系统里面,然后我们开始讲解我埋下的第二个坑点----用户权限划分。
首先我现在是一个普通员工,但是我发现我现在可以进行与管理员一样的操作,可以对数据进行删除和修改,这要是遇到有素质的还好,这要是是一个对公司有意见的,我直接给你全删了,或者给你修改数据,这肯定是不行的。首先我先讲解一个简单的方法-----直接从前端做判断处理
我们在前面写中间件的时候,如果我们能够存储登录的信息的话,然后将这些信息附加到我们每次的请求中去,然后我们通过这些信息进行一个判断,我们是不是可以得出我们想要的界面呢。
auth.py
# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class AuthMiddleware(MiddlewareMixin):
def process_request(self,request):
if request.path_info in ['/login/','/logout/','/image/code/']:
return
info_dict = request.session.get('info')
if info_dict:
# 权限划分板块
request.unicom_userid = info_dict['id']
request.unicom_username = info_dict['name']
request.unicom_role = info_dict['role']
# print(request.unicom_userid,request.unicom_username,request.unicom_role)
return
return redirect('/login/')
def process_response(self,request,response):
return response
就如我刚刚说的,如果我是一个普通员工,那我肯定只能看到普通员工才能看到的画面,如果我是管理员,那么我就应该看到管理员应该看到的。
layout.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
</head>
{#<body background="{% static 'img/image.jpg' %}" STYLE="background-repeat: no-repeat;background-attachment: fixed;background-size: 100% 100%">#}
<body>
<div class="navbar navbar-default">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<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="http://127.0.0.1:8000/">管理系统</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
{% if request.unicom_role == 'user' %}
<li><a href="/user/list/">员工列表</a></li>
<li><a href="/pretty/list/">账号列表</a></li>
{% endif %}
{% if request.unicom_role == 'boss' %}
<li><a href="/depart/list">部门列表</a></li>
<li><a href="/user/list/">员工列表</a></li>
<li><a href="/pretty/list">账号管理</a></li>
{% endif %}
{% if request.unicom_role == 'admin' %}
<li><a href="/user/list/">员工列表</a></li>
<li><a href="/depart/list">部门列表</a></li>
<li><a href="/pretty/list">账号管理</a></li>
<li><a href="/admin/list">用户账号</a></li>
{% endif %}
</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">{{ request.session.info.name }} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="/logout/"> 退出登录 </a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</div>
<div>
{#用于其他html代码进行代码复写#}
{% block content %}
{% endblock %}
</div>
{# 引入js代码 #}
<script src="{% static 'js/jquery-3.7.1.js' %}"></script>
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'laydate/laydate.js' %}"></script>
{% block js %}
{% endblock %}
</body>
</html>
我们通过这样写是不是就能保证每一种级别的员工都只能看到他们自己能看到的,然后我们找一个普通级别的账号试试效果。
那么这初步的用户权限划分就结束了。。。。。
啊?你们不会真的以为结束了吧 ,我还没码完怎么可能就这样结束。。。。。这里面还有坑我的朋友们,现在我们在普通员工页面里面可以发现它是没有部门列表的,如果我现在在里面输入部门列表会发生什么?
可以发现它居然给我们跳转到部门列表了,啊这。。。。。不过细心的朋友可能发现了,这不是跟我们刚刚的登录是一回事嘛,所以我们还是得用到中间件,接下来我们处理用户权限的细节处理。
这里我们首先要对路由进行一个修改,对路由的信息做一个标记
urls.py
from django.contrib import admin
from project_manage import views
from project_manage.views import depart, user, pretty,admin,login,demo,perform,image
urlpatterns = [
# 首页
path('', depart.index, name="index"),
# 部门列表
path('depart/list/', depart.depart_list, name="depart_list"),
# 增加部门
path('depart/add/', depart.depart_add, name="depart_add"),
# 删除部门
path('depart/delete/', depart.depart_delete, name="depart_delete"),
# 修改部门
path('depart/<int:nid>/modify/', depart.depart_modify, name="depart_modify"),
# 员工列表
path('user/list/', user.user_list, name="user_list"),
# 员工删除
# path('user/delete/', views.user_delete),
path('user/<int:nid>/delete/', user.user_delete, name="user_delete"),
# 员工增加
path('user/add/', user.user_add, name="user_add"),
# 员工增加ModelForm
path('user/model/form/add/', user.user_model_form_add, name="user_model_form_add"),
# 员工修改ModelForm
path('user/<int:nid>/modify/', user.user_modify, name="user_modify"),
# 账号列表
path('pretty/list/', pretty.pretty_list, name="pretty_list"),
# 账号添加
path('pretty/add/', pretty.pretty_add, name="pretty_add"),
# 账号编辑
path('pretty/<int:nid>/modify/', pretty.pretty_modify, name="pretty_modify"),
# 账号删除
path('pretty/<int:nid>/delete/', pretty.pretty_delete, name="pretty_delete"),
# 用户账号列表
path('admin/list/', admin.admin_list, name="admin_list"),
# 用户账号添加
path('admin/add/', admin.admin_add, name="admin_add"),
# 用户账号编辑
path('admin/<int:nid>/modify/', admin.admin_modify, name="admin_modify"),
# 用户账号删除
path('admin/<int:nid>/delete/', admin.admin_delete, name="admin_delete"),
# 用户密码重置
path('admin/<int:nid>/reset/', admin.admin_reset, name="admin_reset"),
# 登录页面
path('login/',login.login),
# 退出登录
path('logout/',login.logout),
# 验证码
path('image/code/',login.image_code,name='image_code'),
]
在路由后面对每一个路由命名,然后打开setting.py,在其中新建一个变量用于存储不同级别的用户可以进行的操作
UNICOM_PERMISSION = {
"admin": ["index", "depart_list", "depart_add", "depart_delete", "depart_modify",
"user_list", "user_delete", "user_add", "user_model_form_add", "user_modify",
"pretty_list", "pretty_add", "pretty_modify", "pretty_delete", "admin_list",
"admin_add", "admin_modify", "admin_delete", "admin_reset",'demo_list','demo_add',
'demo_delete','demo_modify', 'demo_content_modify',
'perform_list','perform_add','perform_delete','perform_modify','perform_content_modify',
'image_list','media','image_delete'
],
"boss": ["index", "depart_list", "depart_add", "depart_delete", "depart_modify",
"user_list", "user_delete", "user_add", "user_model_form_add", "user_modify",
"pretty_list", "pretty_add", "pretty_modify", "pretty_delete", "admin_list", "admin_modify"
],
"user": ["index", "user_list", "pretty_list",'perform_list','image_list','media']
}
然后对于中间件重新编写
# -*- coding:utf-8 -*-
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
class AuthMiddleware(MiddlewareMixin):
def process_request(self,request):
if request.path_info in ['/login/','/logout/','/image/code/']:
return
info_dict = request.session.get('info')
if info_dict:
# 权限划分板块
request.unicom_userid = info_dict['id']
request.unicom_username = info_dict['name']
request.unicom_role = info_dict['role']
# print(request.unicom_userid,request.unicom_username,request.unicom_role)
return
return redirect('/login/')
# 权限校验板块
def process_view(self,request,view_func,args,kwargs):
# 无需校验登录和退出 ------测试点
if request.path_info in ['/login/','/logout/','/image/code/']:
return
# print(request.resolver_match)
# 当前用户的角色
role = request.unicom_role
# print(role)
# 角色具备的权限
user_permission_list = settings.UNICOM_PERMISSION[role]
# print(user_permission_list)
if request.resolver_match.url_name in user_permission_list:
# print(request.resolver_match.url_name)
return
return render(request,'error.html')
def process_response(self,request,response):
return response
其中如果发现该用户没有这个权限时,我们就会让其返回到一个错误界面中。
在templates中新建一个error.html文件
error.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>404</title>
<style>
html, body {
height: 100%;
min-height: 450px;
font-size: 32px;
font-weight: 500;
color: #5d7399;
margin: 0;
padding: 0;
border: 0;
}
.content {
height: 100%;
position: relative;
z-index: 1;
background-color: #d2e1ec;
background-image: -webkit-linear-gradient(top, #bbcfe1 0%, #e8f2f6 80%);
background-image: linear-gradient(to bottom, #bbcfe1 0%, #e8f2f6 80%);
overflow: hidden;
}
.snow {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
z-index: 20;
}
.main-text {
padding: 20vh 20px 0 20px;
text-align: center;
line-height: 2em;
font-size: 5vh;
}
.main-text h1 {
font-size: 45px;
line-height: 48px;
margin: 0;
padding: 0;
}
.main-text-a {
height: 32px;
margin-left: auto;
margin-right: auto;
text-align: center;
}
.main-text-a a {
font-size: 16px;
text-decoration: none;
color: #0066CC;
}
.main-text-a a:hover {
color: #000;
}
.home-link {
font-size: 0.6em;
font-weight: 400;
color: inherit;
text-decoration: none;
opacity: 0.6;
border-bottom: 1px dashed rgba(93, 115, 153, 0.5);
}
.home-link:hover {
opacity: 1;
}
.ground {
height: 160px;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
background: #f6f9fa;
box-shadow: 0 0 10px 10px #f6f9fa;
}
.ground:before, .ground:after {
content: '';
display: block;
width: 250px;
height: 250px;
position: absolute;
top: -62.5px;
z-index: -1;
background: transparent;
-webkit-transform: scaleX(0.2) rotate(45deg);
transform: scaleX(0.2) rotate(45deg);
}
.ground:after {
left: 50%;
margin-left: -166.66667px;
box-shadow: -340px 260px 15px #8193b2, -620px 580px 15px #8193b2, -900px 900px 15px #b0bccf, -1155px 1245px 15px #b4bed1, -1515px 1485px 15px #8193b2, -1755px 1845px 15px #8a9bb8, -2050px 2150px 15px #91a1bc, -2425px 2375px 15px #bac4d5, -2695px 2705px 15px #a1aec6, -3020px 2980px 15px #8193b2, -3315px 3285px 15px #94a3be, -3555px 3645px 15px #9aa9c2, -3910px 3890px 15px #b0bccf, -4180px 4220px 15px #bac4d5, -4535px 4465px 15px #a7b4c9, -4840px 4760px 15px #94a3be;
}
.ground:before {
right: 50%;
margin-right: -166.66667px;
box-shadow: 325px -275px 15px #b4bed1, 620px -580px 15px #adb9cd, 925px -875px 15px #a1aec6, 1220px -1180px 15px #b7c1d3, 1545px -1455px 15px #7e90b0, 1795px -1805px 15px #b0bccf, 2080px -2120px 15px #b7c1d3, 2395px -2405px 15px #8e9eba, 2730px -2670px 15px #b7c1d3, 2995px -3005px 15px #9dabc4, 3285px -3315px 15px #a1aec6, 3620px -3580px 15px #8193b2, 3880px -3920px 15px #aab6cb, 4225px -4175px 15px #9dabc4, 4510px -4490px 15px #8e9eba, 4785px -4815px 15px #a7b4c9;
}
.mound {
margin-top: -80px;
font-weight: 800;
font-size: 180px;
text-align: center;
color: #dd4040;
pointer-events: none;
}
.mound:before {
content: '';
display: block;
width: 600px;
height: 200px;
position: absolute;
left: 50%;
margin-left: -300px;
top: 50px;
z-index: 1;
border-radius: 100%;
background-color: #e8f2f6;
background-image: -webkit-linear-gradient(top, #dee8f1, #f6f9fa 60px);
background-image: linear-gradient(to bottom, #dee8f1, #f6f9fa 60px);
}
.mound:after {
content: '';
display: block;
width: 28px;
height: 6px;
position: absolute;
left: 50%;
margin-left: -150px;
top: 68px;
z-index: 2;
background: #dd4040;
border-radius: 100%;
-webkit-transform: rotate(-15deg);
transform: rotate(-15deg);
box-shadow: -56px 12px 0 1px #dd4040, -126px 6px 0 2px #dd4040, -196px 24px 0 3px #dd4040;
}
.mound_text {
-webkit-transform: rotate(6deg);
transform: rotate(6deg);
}
.mound_spade {
display: block;
width: 35px;
height: 30px;
position: absolute;
right: 50%;
top: 42%;
margin-right: -250px;
z-index: 0;
-webkit-transform: rotate(35deg);
transform: rotate(35deg);
background: #dd4040;
}
.mound_spade:before, .mound_spade:after {
content: '';
display: block;
position: absolute;
}
.mound_spade:before {
width: 40%;
height: 30px;
bottom: 98%;
left: 50%;
margin-left: -20%;
background: #dd4040;
}
.mound_spade:after {
width: 100%;
height: 30px;
top: -55px;
left: 0%;
box-sizing: border-box;
border: 10px solid #dd4040;
border-radius: 4px 4px 20px 20px;
}
</style>
</head>
<body translate="no">
<div class="content">
<canvas class="snow" id="snow" width="1349" height="400"></canvas>
<div class="main-text">
<h1>404 天呐!出错了 ~<br><br>您好像去了一个不存在的地方! (灬ꈍ ꈍ灬)</h1>
<div class="main-text-a"><a href="/">< 返回 首页</a></div>
</div>
<div class="ground">
<div class="mound">
<div class="mound_text">404</div>
<div class="mound_spade"></div>
</div>
</div>
</div>
<script>
(function () {
function ready(fn) {
if (document.readyState != 'loading') {
fn();
} else {
document.addEventListener('DOMContentLoaded', fn);
}
}
function makeSnow(el) {
var ctx = el.getContext('2d');
var width = 0;
var height = 0;
var particles = [];
var Particle = function () {
this.x = this.y = this.dx = this.dy = 0;
this.reset();
}
Particle.prototype.reset = function () {
this.y = Math.random() * height;
this.x = Math.random() * width;
this.dx = (Math.random() * 1) - 0.5;
this.dy = (Math.random() * 0.5) + 0.5;
}
function createParticles(count) {
if (count != particles.length) {
particles = [];
for (var i = 0; i < count; i++) {
particles.push(new Particle());
}
}
}
function onResize() {
width = window.innerWidth;
height = window.innerHeight;
el.width = width;
el.height = height;
createParticles((width * height) / 10000);
}
function updateParticles() {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#f6f9fa';
particles.forEach(function (particle) {
particle.y += particle.dy;
particle.x += particle.dx;
if (particle.y > height) {
particle.y = 0;
}
if (particle.x > width) {
particle.reset();
particle.y = 0;
}
ctx.beginPath();
ctx.arc(particle.x, particle.y, 5, 0, Math.PI * 2, false);
ctx.fill();
});
window.requestAnimationFrame(updateParticles);
}
onResize();
updateParticles();
}
ready(function () {
var canvas = document.getElementById('snow');
makeSnow(canvas);
});
})();
</script>
</body>
</html>
因为本人对于前端代码写的不是特别好,因此在网上找的一个错误界面代码,但是忘记标记在哪找的了,如果涉及到抄袭不允许的话,作者可以私聊我,我可以进行删除,sorry~。
这样我们重新运行我们的代码然后重新进入部门列表
因此我们的权限划分就到此结束啦。
最后再祝大家新年快乐啦。