粒度控制
权限无非就增,删,改,查四种操作,粒度控制指的是在对每个表的管理中,根据用户的权限来显示用户可以进行的操作。
简单的实现
在之前的 RBAC 权限管理的基础上能够简单实现该功能。
因为已经在 session 中存储了关于权限的 url 列表,所以在模板层的 a 标签处进行判断,如果对应的 url 在 permission_list 中时进行显示,否则不显示。
{% if "users/add" in permissions_list%}
<a class="btn btn-primary" href="/users/add">添加用户</a>
{% endif %}
该方法虽然简单但并不方便,因为每次判断显示与否都需要跟表挂钩。好的方法应该摆脱表控制,在任何模板中都能够方便使用。
实现方案 2
更改数据库结构
实际上,可以对操作进行更细致的划分,比如,将操作作为动作字段加入到权限表中,并将权限表与权限组表关联,权限组表存储对哪类数据进行的操作。
class Permission(models.Model):
title=models.CharField(max_length=32)
url=models.CharField(max_length=32)
action=models.CharField(max_length=32,default="")
group=models.ForeignKey("PermissionGroup",default=1)
def __str__(self):return self.title
class PermissionGroup(models.Model):
title = models.CharField(max_length=32)
def __str__(self): return self.title
登录验证
因为增加了一张表和一些字段,使得我们能用到的信息更多了。更多的信息有助于更方便的实现所需要的功能。
更多关于权限的信息,获取到的形式都有变化:
permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct()
permissions 的形式:
permissions:
[
{'permissions__url': '/users/add/',
'permissions__group_id': 1,
'permissions__action': 'add'},
{'permissions__url': '/roles/',
'permissions__group_id': 2,
'permissions__action': 'list'},
{'permissions__url': '/users/delete/(\d+)',
'permissions__group_id': 1,
'permissions__action': 'delete'},
{'permissions__url': 'users/edit/(\d+)',
'permissions__group_id': 1,
'permissions__action': 'edit'}
]
之前所实现的过程中使用的是权限列表 permission_list ,其中只能存储权限 url ,现在能获取更多的信息了,自然,注册到 session 中的有关权限的信息也可以以更方便的形式来存贮:
permission_dict
{
1: {
'urls': ['/users/', '/users/add/', '/users/delete/(\d+)', 'users/edit/(\d+)'],
'actions': ['list', 'add', 'delete', 'edit']},
2: {
'urls': ['/roles/'],
'actions': ['list']}
}
将从数据库中取出的权限数据转成我们想要形式的代码如下:
# 以字典的形式存储动作信息,此种方式能够存储更多内容
permission_dict = {}
for item in permissions:
gid = item.get('permissions__group_id')
# 以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"])
request.session["permission_dict"] = permission_dict
中间件权限校验
进行权限校验时,从在 session 注册的字典中找到相关url信息进行校验并将权限允许的动作存到request中,方便在视图层的使用。
permission_dict = request.session.get("permission_dict")
# 从字典中找到相关url信息进行校验并将权限允许的动作存到request中方便以后使用
for permission in permission_dict.values():
urls = permission['urls']
for reg in urls:
reg = "^%s$"%reg
ret = re.match(reg, current_path)
# 如果匹配到权限路径,将相关的动作存储到session中
if ret:
request.actions = permission["actions"]
return None
return HttpResponse("没有访问权限!")
视图层和模板层的收尾工作
在 request 中存储了用户能够进行的操作动作之后,在模板层可以这么使用:
{% if "add" in actions%}
<a class="btn btn-primary" href="/users/add">添加用户</a>
{% endif %}
为了方便,可以将所有的类似 if "action" in actions
的判断封装到一个类中,方便调用。
class Per(object):
def __init__(self, actions):
self.actions = actions
def add(self):
return "add" in self.actions
def delete(self):
return "delete" in self.actions
def edit(self):
return "edit" in self.actions
def list(self):
return "list" in self.actions
视图层代码:
def users(request):
users_list = models.User.objects.all()
id = request.session.get("user_id")
user = models.User.objects.filter(id=id).first()
permission_list = request.session.get("permission_list")
# actions = request.session.get("actions")
per = Per(request.actions)
return render(request, "users.html", locals())
模板层代码:
{% extends 'base.html' %}
{% block con %}
<h4>用户列表</h4>
{% if per.add %}
<a class="btn btn-primary" href="/users/add">添加用户</a>
{% endif %}
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>角色</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in users_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ user.name }}</td>
<td>
{% for role in user.roles.all %}
{{ role.title }}
{% endfor %}
</td>
<td>
{% if per.delete %}
<a class="btn btn-danger" href="/users/delete/{{ user.pk }}">删除</a>
{% endif %}
{% if per.edit %}
<a class="btn btn-warning" href="/users/edit/{{ user.pk }}">编辑</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
不同用户登录下不同权限显示不同:
GitHub 地址:https://github.com/protea-ban/oldboy/tree/master/9day83/rbacDemo