Fork me on GitHub

django权限组件开发

什么是权限?
权限 就是对 软件系统 中 各种资源 的 访问和操作的控制!

什么是资源?
在软件系统中,数据库、内存、硬盘里数据都是资源,资源就是数据!

动作
资源本身是静态的, 必须通过合适的动作对其进行访问和操作,我们说要控制权限,其实本质上是要对访问 软件中各种数据资源的动作进行控制

动作又可以分为2种:

  1. 资源操作动作:访问和操作各种数据资源,比如访问数据库或文件里的数据

  2. 业务逻辑事件动作:访问和操作的目的不是数据源本身,而是借助数据源而产生的一系列业务逻辑,比如批量往远程 主机上上传一个文件,你需要从数据库中访问主机列表,但你真正要操作的是远程的主机,这个远程的主机,严格意义上来并不是你的数据资源,而是这个资源代表的实体.

权限授权

  1. 权限的使用者可以是具体的个人、亦可以是其它程序, 这都没关系,我们可以把权限的授权主体,统称为用户, 无论这个用户后面是具体的人,还是一个程序,对权限控制组件来讲,都不影响.
  2. 权限必然是需要分组的,把一组权限 分成一个组,授权给特定的一些用户,分出来的这个组,就可以称为角色.
  3. 权限 应该是可以叠加的!

权限表结构设计

rbac(Role-Based Access Control)基于角色的权限访问控制
一个人可以有多个角色.
一个角色可以有多个人.
一个角色可以有多个权限.
一个权限可以分配给多个角色.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from django.db import models


class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)

def __str__(self):
return self.title


class Role(models.Model):
"""
角色
"""
title = models.CharField(verbose_name='角色名称', max_length=32)
permissions = models.ManyToManyField(verbose_name='拥有的所有权限', to='Permission', blank=True)

def __str__(self):
return self.title


class UserInfo(models.Model):
"""
用户表
"""
name = models.CharField(verbose_name='用户名', max_length=32)
password = models.CharField(verbose_name='密码', max_length=64)
email = models.CharField(verbose_name='邮箱', max_length=32)
roles = models.ManyToManyField(verbose_name='拥有的所有角色', to='Role', blank=True)

def __str__(self):
return self.name

权限校验

在用户成功登录之后, 从权限表中取出其权限, 存入session中, 在用户访问url的时候进行校验, 判断用户是否有权限

权限注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def login(request):
if request.method == "POST":

user = request.POST.get("name")
pwd = request.POST.get("pwd")

user = User.objects.filter(name=user, password=pwd).first()
if user:
############################### 在session中注册用户ID######################
request.session["user_id"] = user.pk

###############################在session注册权限列表##############################

# 查询当前登录用户的所有角色
# ret=user.roles.all()
# print(ret)# <QuerySet [<Role: 保洁>, <Role: 销售>]>

# 查询当前登录用户的所有权限
initial_session(user, request)

return HttpResponse("登录成功!")

return render(request, "login.html")


def initial_session(user,request):
permissions = user.roles.all().values("permissions__url").distinct()

permission_list = []

for item in permissions:
permission_list.append(item["permissions__url"])
print(permission_list)

request.session["permission_list"] = permission_list

权限校验

在需要进行权限控制的视图函数中判断是否有权限

1
2
3
4
5
6
7
8
9
10
11
12
13
permission_list = request.session['permission_list']

current_path = request.path_info
flag = False
for permission in permission_list:
permission = "^%s$"%permission

ret = re.match(permission, current_path)
if ret:
flag = True
break
if not flag:
return HttpResponse("没有访问权限")

基于中间件的权限校验

有些url是不需要进行权限控制的, 如login, logout, admin, reg
可以设置一个白名单, 将这些不需要进行权限控制的url放在白名单中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import re
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect

class ValidPermission(MiddlewareMixin):

def process_request(self,request):


# 当前访问路径
current_path = request.path_info

# 检查是否属于白名单
valid_url_list=["/login/","/reg/","/admin/.*"]

for valid_url in valid_url_list:
ret=re.match(valid_url,current_path)
if ret:
return None


# 校验是否登录

user_id=request.session.get("user_id")

if not user_id:
return redirect("/login/")


# 校验权限
permission_list = request.session.get("permission_list",[]) # ['/users/', '/users/add', '/users/delete/(\\d+)', 'users/edit/(\\d+)']

flag = False
for permission in permission_list:

permission = "^%s$" % permission

ret = re.match(permission, current_path)
if ret:
flag = True
break
if not flag:
return HttpResponse("没有访问权限!")

return None

将这个中间件注入到MIDDLEWARE中即可实现简单的权限访问控制

后台admin的控制

最简单的一种

视图函数中获取权限列表, 将其传至模板中, 在模板中来进行判断是否显示按钮,
这样当不同的用户进行访问的时候, 看到的就是不同的页面

1
2
3
4
5
6
7
def users(request):
user_list = User.objects.all()
permission_list = request.session.get("permission_list")
id = request.session.get("user_id")
user = User.objects.filter(id=id).first()

return render(request, "users.html", locals())

1
2
3
{% if '/users/add/' in permission_list %}
<a href="/users/add/" class="btn btn-primary">添加用户</a>
{% endif %}

修改models

给权限添加一个动作(增删改查), 再给权限分个组(一个组管一个model)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)

action = models.CharField(max_length=32, default="", verbose_name="动作", help_text="add, del, change等")
group = models.ForeignKey("PermissionGroup", blank=True, null=True, verbose_name="权限组", on_delete=models.CASCADE)

def __str__(self):
return self.title

class Meta:
verbose_name = "权限"
verbose_name_plural = verbose_name


class PermissionGroup(models.Model):
title = models.CharField(max_length=32, verbose_name="权限组名称")

def __str__(self):
return self.title

class Meta:
verbose_name = "权限组"
verbose_name_plural = verbose_name

修改一下登录成功时的session设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def initial_session(user, request):
# permissions = user.roles.all().values("permissions__url").distinct()
#
# permission_list = []
#
# for item in permissions:
# permission_list.append(item["permissions__url"])
# print(permission_list)
#
# request.session["permission_list"] = permission_list

permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct()
print("permissions", permissions)

permission_dict = {}
for item in permissions:
gid = item.get('permissions__group_id')

if not gid in permission_dict:

permission_dict[gid] = {
"urls": [item["permissions__url"], ],
"actions": [item["permissions__action"], ]
}
else:
permission_dict[gid]["urls"].append(item["permissions__url"])
permission_dict[gid]["actions"].append(item["permissions__action"])

print(permission_dict)
request.session['permission_dict'] = permission_dict

中间件也需要修改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class ValidPermission(MiddlewareMixin):

def process_request(self, request):

# 当前访问路径
current_path = request.path_info

# 检查是否属于白名单
valid_url_list = ["/login/", "/reg/", "/admin/.*"]

for valid_url in valid_url_list:
ret = re.match(valid_url, current_path)
if ret:
return None

# 校验是否登录

user_id = request.session.get("user_id")

if not user_id:
return redirect("/login/")

# 校验权限
# permission_list = request.session.get("permission_list", []) # ['/users/', '/users/add', '/users/delete/(\\d+)', 'users/edit/(\\d+)']

# flag = reg(request, current_path)
#
# if not flag:
# return HttpResponse("没有访问权限")


# 校验权限2
permission_dict=request.session.get("permission_dict")

for item in permission_dict.values():
urls=item['urls']
for reg in urls:
reg="^%s$"%reg
ret=re.match(reg,current_path)
if ret:
print("actions",item['actions'])
request.actions=item['actions']
return None

return HttpResponse("没有访问权限!")

在视图函数中进行一个简单的设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Per(object):
def __init__(self,actions):
self.actions=actions
def add(self):
return "add" in self.actions
def delete(self):
return "del" in self.actions
def edit(self):
return "change" in self.actions
def list(self):
return "list" in self.actions


def users(request):
user_list = User.objects.all()
# permission_list = request.session.get("permission_list")
id = request.session.get("user_id")
user = User.objects.filter(id=id).first()

per = Per(request.actions)
return render(request, "users.html", locals())

模板中的使用方法

1
2
3
4
5
6
7

{% if per.delete %}
<a href="" class="btn btn-danger">删除</a>
{% endif %}
{% if per.edit %}
<a href="" class="btn btn-warning">编辑</a>
{% endif %}

这样也能实现访问控制

菜单显示

initial_session函数中注入菜单

1
2
3
4
5
6
7
8
9
10
11
# 注册菜单权限
permissions = user.roles.all().values("permissions__url","permissions__action","permissions__group__title").distinct()
print("permissions",permissions)

menu_permission_list=[]
for item in permissions:
if item["permissions__action"]=="list":
menu_permission_list.append((item["permissions__url"],item["permissions__group__title"]))

print(menu_permission_list)
request.session["menu_permission_list"]=menu_permission_list

编写一个生成菜单的标签

1
2
3
4
5
6
@register.inclusion_tag("perm/menu.html")
def get_menu(request, ):
# 获取当前用户可以放到菜单栏中的权限
menu_permission_list = request.session["menu_permission_list"]

return {"menu_permission_list": menu_permission_list}

menu.html

1
2
3
4
5
<div>
{% for item in menu_permission_list %}
<p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
{% endfor %}
</div>

base模板中的使用方法

1
2
3
<div class="menu">
{% get_menu request %}
</div>

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%