1. 程式人生 > >django自定義rbac許可權元件(二級選單)

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 %}