• django自定义rbac权限组件(二级菜单)


    一、目录结构

    二、表结构设计

    model.py

    from django.db import models
    
    # Create your models here.
    
    
    class Menu(models.Model):
        """菜单表 一级菜单"""
        title = models.CharField(max_length=32)
        icon = models.CharField(max_length=64, null=True, blank=True, verbose_name='图标')
    
        def __str__(self):
            return self.title
    
    class Permission(models.Model):
        """
        权限表
        可以做二级菜单的权限   menu 关联 菜单表
        不可以做菜单的权限    menu=null
        """
        url = models.CharField(max_length=32, verbose_name='权限')
        title = models.CharField(max_length=32, verbose_name='标题')
        menu = models.ForeignKey("Menu",null=True, blank=True, verbose_name="所属菜单",on_delete=models.CASCADE)
    
        class Meta:
            # 这个选项是指定,模型的复数形式是什么,比如:
            # verbose_name_plural = "学校"
            # 如果不指定Django会自动在模型名称后加一个’s’
            verbose_name_plural = '权限表'
            verbose_name = '权限'
    
        def __str__(self):
            return self.title
    
    
    class Role(models.Model):
        """
        角色表
        """
        name = models.CharField(max_length=32, verbose_name='名称')
        permissions = models.ManyToManyField('Permission', verbose_name='角色拥有的权限',blank=True)
    
        def __str__(self):
            return self.name
    
    
    
    class User(models.Model):
        """
        用户表
        """
        name = models.CharField(max_length=32, verbose_name='名称')
        password = models.CharField(max_length=32, verbose_name='密码')
        roles = models.ManyToManyField('Role', verbose_name='用户拥有的角色',blank=True)
    
        def __str__(self):
            return self.name

    三、权限信息初始化

    用户登陆成功后保留权限信息与菜单信息

    service.permission.py

    from django.conf import settings
    
    
    def init_permisson(request, obj):
        """
            权限信息的初识化
            保存权限和菜单的信息
            :param request:
            :param obj:
            :return:
         """
        # 登陆成功,保存权限的信息(可能存在创建了角色没有分配权限,有的用户拥有多个角色权限重复的要去重.distinct())
        ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                            'permissions__title',
                                                                            'permissions__menu__title',
                                                                            'permissions__menu__icon',
                                                                            'permissions__menu_id'
                                                                            ).distinct()
    
        # 存放权限信息
        permission_list = []
        # 存放菜单信息
        menu_dict = {}
        for item in ret:
            # 将所有的权限信息添加到permission_list
            permission_list.append({'url': item['permissions__url']})
    
            # 构造菜单的数据结构
            menu_id = item.get('permissions__menu_id')
    
            # 表示当前的权限是不做菜单的权限
            if not menu_id:
                continue
    
            # 可以做菜单的权限
            if menu_id not in menu_dict:
                menu_dict[menu_id] = {
                    'title': item['permissions__title'],  # 一级菜单标题
                    'icon': item['permissions__menu__icon'],
                    'children': [
                        {'title': item['permissions__menu__title'], 'url': item['permissions__url']},
                    ]
                }
            else:
                menu_dict[menu_id]['children'].append(
                    {'title': item['permissions__menu__title'], 'url': item['permissions__url']})
    
        # print(menu_dict)
        # 保留权限信息到session(因为session可以存到内存中,提高工作效率)中
        request.session[settings.PERMISSION_SESSION_KEY] = permission_list
    
        # 保存菜单信息
        request.session[settings.PERMISSION_MENU_KEY] = menu_dict

    四、中间件中权限校验

    菜单数据结构构造

    注意构造菜单的数据结构,将查询出的元数据构造为分级的数据结构。

    # 元数据
    data = [{
        'permissions__url': '/customer/list/',
        'permissions__title': '客户列表',
        'permissions__menu__title': '信息列表',
        'permissions__menu__icon': 'fa-code-fork',
        'permissions__menu_id': 1
    },
        {
            'permissions__url': '/customer/list/',
            'permissions__title': '用户列表',
            'permissions__menu__title': '信息列表',
            'permissions__menu__icon': 'fa-code-fork',
            'permissions__menu_id': 1
        }, {
            'permissions__url': '/customer/add/',
            'permissions__title': '增加客户',
            'permissions__menu__title': None,
            'permissions__menu__icon': None,
            'permissions__menu_id': None
        }, {
            'permissions__url': '/customer/edit/(\d+)/',
            'permissions__title': '编辑客户',
            'permissions__menu__title': None,
            'permissions__menu__icon': None,
            'permissions__menu_id': None
        }]
    # 目标数据
    { 
      1:{
          'title':'信息列表',
          'icon':'fa-code-fork',
        'children': [
            {'title': '客户列表','url':'/customer/list/ },
            {'title': '用户列表','url':'/customer/list/ }
        ]
          
      }
    
    }

    middlewares.rbac.py

    from django.conf import settings
    
    
    def init_permisson(request, obj):
        """
            权限信息的初识化
            保存权限和菜单的信息
            :param request:
            :param obj:
            :return:
         """
        # 登陆成功,保存权限的信息(可能存在创建了角色没有分配权限,有的用户拥有多个角色权限重复的要去重.distinct())
        ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
                                                                            'permissions__title',
                                                                            'permissions__menu__title',
                                                                            'permissions__menu__icon',
                                                                            'permissions__menu_id'
                                                                            ).distinct()
    
        # 存放权限信息
        permission_list = []
        # 存放菜单信息
        menu_dict = {}
        for item in ret:
            # 将所有的权限信息添加到permission_list
            permission_list.append({'url': item['permissions__url']})
    
            # 构造菜单的数据结构
            menu_id = item.get('permissions__menu_id')
    
            # 表示当前的权限是不做菜单的权限
            if not menu_id:
                continue
    
            # 可以做菜单的权限
            if menu_id not in menu_dict:
                menu_dict[menu_id] = {
                    'title': item['permissions__title'],  # 一级菜单标题
                    'icon': item['permissions__menu__icon'],
                    'children': [
                        {'title': item['permissions__menu__title'], 'url': item['permissions__url']},
                    ]
                }
            else:
                menu_dict[menu_id]['children'].append(
                    {'title': item['permissions__menu__title'], 'url': item['permissions__url']})
    
        # print(menu_dict)
        # 保留权限信息到session(因为session可以存到内存中,提高工作效率)中
        request.session[settings.PERMISSION_SESSION_KEY] = permission_list
    
        # 保存菜单信息
        request.session[settings.PERMISSION_MENU_KEY] = menu_dict

    五、自定义inclusion_tag

    用于定义html片段,实现动态数据传入。

    文件包必须叫templatetags

    templatetags.rbac.py

    from django import template
    from django.conf import settings
    import re
    
    register = template.Library()
    
    
    @register.inclusion_tag('rbac/menu.html')
    def menu(request):
        # # 取到存在session 里的菜单权限信息信息(一级菜单时)
        # menu_list = request.session.get(settings.PERMISSION_MENU_KEY)
        #
        # for item in menu_list:
        #     # 正则匹配当前路径
        #     if re.match('^{}$'.format(item['url']), request.path_info):
        #         # 添加一个点击样式
        #         item['class'] = 'active'
        #         break
        # return {"menu_list": menu_list}
    
        # 二级菜单时
        menu_dict = request.session.get(settings.PERMISSION_MENU_KEY)
    
        return {'menu_list': menu_dict.values()}

     注意:为了不把数据写死,便于维护,存在session中的权限相关配置写在setting中

    # session中保留权限key
    PERMISSION_SESSION_KEY = 'permissions'
    # 保留菜单信息key
    PERMISSION_MENU_KEY = 'menus'
    # 白名单
    WHITE_LIST = [
        r'^/login/$',
        r'^/reg/$',
        r'^/admin/.*',
    ]

    在templates模板中应用菜单

    html

       {% load rbac %}
    {#            <!--应用inclusion_tag('rbac/menu.html')-->#}
                {% menu request %}
  • 相关阅读:
    getfacl语法2
    getfacl
    setfacl语法2
    setfacl语法
    特殊权限语法2
    特殊权限语法
    vim语法
    locate语法
    find语法
    dos语法
  • 原文地址:https://www.cnblogs.com/zwq-/p/10171124.html
Copyright © 2020-2023  润新知