• rbac之 权限粒度控制到按钮级别


    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
    Permission的最终形态。(以后想改 再改喽)

    然后,为了比较的方便。我对 登录时的,保存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
    init_permission初始化,权限列表更改为,权限字典!

    相应的中间件的地方,也作了修改。 以前因为是一个列表中嵌套的字典格式。 但现在是一个大字典了。 所以 现在循环的是一个字典。

    需要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("无权访问")
    对 rbac.py 中间件,进行修改

    最后就是 判断当前这个按钮的别名。 是否在当前用户的权限中有,这个别名:
      解释: 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
    rbac_tags 自第一过滤器

    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>
    对每一个按钮的 地方都加上,这样的一个判断。就OK啦

    然后, 有一点。 选项这里, 编辑和删除的操作。
    这里应该是  如果当前用户,有 编辑 或 删除任何一个,权限。那么就应该有选项 的 展示。 如果都没有,那么就应该是空的。
    依然使用 这个 过滤器:

    <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 完美。

     

  • 相关阅读:
    读 Kafka 源码写优雅业务代码:配置类
    如何安装FTP服务器,并实现文件共享
    Merge into用法总结
    Insomnia 跟 Postman 类似的软件
    iOS dealloc中初始化weak指针崩溃防护
    Centos7安装febootstrap
    获取 linux 系统 CPU、内存、磁盘 IO 等信息的脚本
    Git本地远程仓库
    网络及服务故障的排查思路
    Git配置远程仓库(密匙链接)
  • 原文地址:https://www.cnblogs.com/chengege/p/10703633.html
Copyright © 2020-2023  润新知