rbac之 权限粒度控制到按钮级别: 这里的意思就是 如果当前用户,没有这个权限。 那么这个相对应的这个按钮的权限, 就不应该展示。看都不能给看到。
思路:
为每一个权限,设置一个别名。 这里是这的别名。 要与 路由控制器中的,每条路径的 别名保持一直
模板中每一个按钮标签的位置,进行 if 判断。 判断这个别名是否在当前用户的,权限字典中。 如果有显示这个按钮。如果没有那就不显示
数据表,进行更改:
class Permission(models.Model):
"""
权限表 一级菜单的表
"""
title = models.CharField(verbose_name='标题', max_length=32)
url = models.CharField(verbose_name='含正则的URL', max_length=128)
name = models.CharField(verbose_name="权限别名", max_length=32, unique=True)
menu = models.ForeignKey(verbose_name="所属菜单", to="Menu", null=True, blank=True, on_delete=models.CASCADE)
pid = models.ForeignKey(verbose_name="关联权限", help_text="对于非菜单权限,需要确定当前权限归属于哪一个,父权限",
to="Permission", null=True, blank=True, on_delete=models.CASCADE, )
def __str__(self):
return self.title
然后,为了比较的方便。我对 登录时的,保存session权限列表的步骤。 进行更改,让他变成一个字典 key 就是 每个权限的别名 name字段
def init_permission(current_user, request): ''' 二级菜单,实现 :param current_user: 当前请求 用户对象 :param request: 当前请求 数据 :return: ''' # 2. 权限 初始化 # 根据当前用户信息,获取当前用户所拥有的所有的权限(queryset对象 是不能直接放入,session中的) permission_queryset = current_user.roles.filter(permissions__isnull=False) .values("permissions__id", "permissions__url", "permissions__title", "permissions__name", "permissions__pid_id", "permissions__pid__title", "permissions__pid__url", "permissions__menu_id", "permissions__menu__icon", "permissions__menu__title", ).distinct() # 获取权限 和 菜单信息。 权限放在权限列表,菜单放在菜单列表 menu_dict = {} permission_dict = {} for item in permission_queryset: permission_dict[item.get("permissions__name")] = { "id": item.get("permissions__id"), "title": item.get("permissions__title"), "url": item.get("permissions__url"), "paren_id": item.get("permissions__pid_id"), "paren_title": item.get("permissions__pid__title"), "paren_url": item.get("permissions__pid__url"), } menu_id = item.get("permissions__menu_id") if not menu_id: continue node = {"id": item.get("permissions__id"), "title": item.get("permissions__title"), "url": item.get("permissions__url")} if menu_id in menu_dict: menu_dict[menu_id]["children"].append(node) else: menu_dict[menu_id] = { "title": item.get("permissions__menu__title"), "icon": item.get("permissions__menu__icon"), "children": [node] } request.session[settings.PERMISSIONS_SESSION_KEY] = permission_dict request.session[settings.MENU_SESSION_KEY] = menu_dict
相应的中间件的地方,也作了修改。 以前因为是一个列表中嵌套的字典格式。 但现在是一个大字典了。 所以 现在循环的是一个字典。
需要for item in permission_dict.values(): 直接去循环这个字典的 values 就可以了!其他的 还是原来的配方,还是原来的味道。
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse from django.conf import settings import re class RbacMiddleware(MiddlewareMixin): '''用户权限信息的校验''' def process_request(self, request): '''当用户请求进入时 触发执行''' ''' 1. 获取当前用户请求的url 2. 获取当前用户在session中保存的 权限列表 [......] 3. 当前请求url 在 session中, 就可以,进行访问 ''' current_url = request.path_info for valid_url in settings.VALID_URL_LIST: if re.match(valid_url, current_url): return None permission_dict = request.session.get(settings.PERMISSIONS_SESSION_KEY) if not permission_dict: return HttpResponse("您没有访问权限...请联系管理员") flag = False url_record = [ {"title": "首页", "url": "#"} ] for item in permission_dict.values(): reg = "^%s$" % item.get("url") if re.match(reg, current_url): flag = True request.current_selected_permission = item.get("paren_id") or item.get("id") if not item.get("paren_id"): url_record.extend([{"title": item.get("title"), "url": item.get("url"), "class": "active"}]) else: url_record.extend([ {"title": item.get("paren_title"), "url": item.get("paren_url")}, {"title": item.get("title"), "url": item.get("url"), "class": "active"}, ]) request.url_record = url_record break if not flag: return HttpResponse("无权访问")
最后就是 判断当前这个按钮的别名。 是否在当前用户的权限中有,这个别名:
解释: 1. 进行判断两个参数, 第一个是当前这个按钮所指向的 url 他的别名好说。 直接写字符串就行。
然后就是,当前用户的权限信息了。 我保存在了 ssession 里面。 具体的就是:
request.session[settings.PERMISSIONS_SESSION_KEY] = permission_dict ( 以别名为键的 权限字典 )
我进行判断时,大概就是这个样子:
{% if "costomer_add" in request.session.permissions_url_list_key%}
.........
{%endif%}
如果我想要直接进行,判断也是可以的, 但是我的初衷 这个 session-key 是可以在settings中进行配置的。 so我们来自定义一个 模板过滤器吧,因为模板过滤器是可以进行逻辑判断的 另外几个就不行 所以使用 @register.filter 没毛病:
这个过滤器接收 name 和 request 两个参数:
@register.filter def has_permission(request, name): ''' :param request: request对象 :param name: 当前权限的别名 :return: ''' if name in request.session.get(settings.PERMISSIONS_SESSION_KEY): return True 如果当前的name别名,在我保存的字典中,return Ture 否则 默认返回 None
ok 过滤器完成: 模板的使用,就简单了:
{% if request|has_permission:"customer_add" %}
...........
{% endif %}
这是一种使用的规范,模板过滤器只接收 最多两个参数。 | 之前的时第一个参数。 |之后是这个过滤器的名字 : 之后的是第二个参数。 这中间不要加空格
{% extends 'layout.html' %} {% load rbac_tags %} <div class="btn-group" style="margin: 5px 0"> {% if request|has_permission:"customer_add" %} <a class="btn btn-default" href="/customer/add/"> <i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户 </a> {% endif %} {% if request|has_permission:"customer_aimport" %} <a class="btn btn-default" href="/customer/import/"> <i class="fa fa-file-excel-o" aria-hidden="true"></i> 批量导入 </a> {% endif %} </div>
然后, 有一点。 选项这里, 编辑和删除的操作。
这里应该是 如果当前用户,有 编辑 或 删除任何一个,权限。那么就应该有选项 的 展示。 如果都没有,那么就应该是空的。
依然使用 这个 过滤器:
<table class="table table-bordered table-hover"> <thead> <tr> <th>ID</th> <th>客户姓名</th> <th>年龄</th> <th>邮箱</th> <th>公司</th> {% if request|has_permission:"customer_edit" or request|has_permission:"customer_del" %} <th>选项</th> {% endif %} </tr> </thead> <tbody> {% for row in data_list %} <tr> <td>{{ row.id }}</td> <td>{{ row.name }}</td> <td>{{ row.age }}</td> <td>{{ row.email }}</td> <td>{{ row.company }}</td> <td> {% if request|has_permission:"customer_edit" %} <a style="color: #333333;" href="/customer/edit/{{ row.id }}/"> <i class="fa fa-edit" aria-hidden="true"></i></a> {% endif %} {% if request|has_permission:"customer_del" %} <a style="color: #d9534f;" href="/customer/del/{{ row.id }}/"><i class="fa fa-trash-o"></i></a> {% endif %} </td> </tr> {% endfor %} </tbody>
ok 完美。