二十九、RBAC+动态菜单

role base access control 基于角色的权限控制

1、Models

from django.db import models

class User(models.Model):
    name = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

    class Meta:
        verbose_name_plural = '用户表'

    def __str__(self):
        return self.name

class Role(models.Model):
    caption = models.CharField(max_length=32)

    class Meta:
        verbose_name_plural = '角色表'

    def __str__(self):
        return self.caption

class User2Role(models.Model):
    u = models.ForeignKey(User, on_delete=models.CASCADE)
    r = models.ForeignKey(Role, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '用户角色表'
        unique_together = [
            ('u', 'r'),
        ]

    def __str__(self):
        return '%s-%s'%(self.u,self.r)

class Permission(models.Model):
    caption = models.CharField(max_length=32, unique=True)
    url = models.CharField(max_length=128, unique=True)
    menu = models.ForeignKey('Menu', on_delete=models.CASCADE, blank=True, null=True)

    class Meta:
        verbose_name_plural = 'url表'
        unique_together = [
            ('caption','url'),
        ]

    def __str__(self):
        return self.caption + self.url

class Action(models.Model):
    caption = models.CharField(max_length=32)
    code = models.CharField(max_length=32)

    class Meta:
        verbose_name_plural = '动作表'

    def __str__(self):
        return self.caption + ":" + self.code

class Permission2Action(models.Model):
    p = models.ForeignKey(Permission, on_delete=models.CASCADE)
    a = models.ForeignKey(Action, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '权限表'
        unique_together = [
            ('p','a'),
        ]

    def __str__(self):
        return '%s-%s:%s-%s'%(self.p.caption, self.a.caption, self.p.url, self.a.code)

class Permission2Action2Role(models.Model):
    p2a = models.ForeignKey(Permission2Action, on_delete=models.CASCADE)
    r = models.ForeignKey(Role, on_delete=models.CASCADE)

    class Meta:
        verbose_name_plural = '角色权限表'
        unique_together = [
            ('p2a', 'r'),
        ]

    def __str__(self):
        return '%s:%s'%(self.r.caption, self.p2a)

class Menu(models.Model):
    caption = models.CharField(max_length=32)
    parent = models.ForeignKey('self', on_delete=models.CASCADE, blank=True,null=True)

    class Meta:
        verbose_name_plural = '菜单表'

    def __str__(self):
        return self.caption

2、权限、菜单

class MenuHelper():
    def __init__(self, request, username):
        self.permission2action_dict = None
        self.menu_leaf_list = None
        self.menu_list = None
        self.__menu_str = None
        self.username = username
        self.current_url = request.path_info
        self.request = request
        self.session_data()

    def session_data(self):
        # 获取当前用户的所有权限
        # 获取在菜单中显示的权限
        # 获取所有菜单
        # 放置session中
        info_session_key = 'permission_info'
        permission2action_dict_key = 'permission2action_dict'
        menu_leaf_list_key = 'menu_leaf_list'
        menu_list_key = 'menu_list'
        permission_dict = self.request.session.get(info_session_key)
        if permission_dict:
            self.permission2action_dict = permission_dict[permission2action_dict_key]
            self.menu_leaf_list = permission_dict[menu_leaf_list_key]
            self.menu_list = permission_dict[menu_list_key]
        else:
            roles = models.Role.objects.filter(user2role__u__name=self.username).all()
            # p = models.Permission2Action2Role.objects.filter(r__in=role).values('p2a')
            permission2action = models.Permission2Action.objects. \
                filter(permission2action2role__r__in=roles). \
                distinct().values('p__url', 'a__code')
            permission2action_dict = {}
            for pa in permission2action:
                url = pa['p__url']
                code = pa['a__code']
                if url in permission2action_dict:
                    permission2action_dict[url].append(code)
                else:
                    permission2action_dict[url] = [code, ]

            menu_leaf_list = list(models.Permission2Action.objects.filter(permission2action2role__r__in=roles). \
                                  exclude(p__menu__isnull=True). \
                                  distinct().values('p__id', 'p__caption', 'p__url', 'p__menu'))
            menus = list(models.Menu.objects.values('id', 'caption', 'parent_id'))
            self.request.session[info_session_key] = {
                permission2action_dict_key: permission2action_dict,
                menu_leaf_list_key: menu_leaf_list,
                menu_list_key: menus,
            }
            self.permission2action_dict = permission2action_dict
            self.menu_leaf_list = menu_leaf_list
            self.menu_list = menus

    def menu_data_list(self):
        url_parent_id = None
        m2_dict = {}
        for i in self.menu_leaf_list:
            item = {
                'caption': i['p__caption'],
                'url': i['p__url'],
                'parent_id': i['p__menu'],
                'child': [],
                'status': True,
                'open': False,
            }
            key = item['parent_id']
            if key in m2_dict:
                m2_dict[key].append(item)
            else:
                m2_dict[key] = [item, ]
            if re.match(item['url'], self.current_url):
                url_parent_id = item['parent_id']
                item['open'] = True

        menu_dict = {}
        for menu in self.menu_list:
            menu['child'] = []
            menu['status'] = False
            menu['open'] = False
            menu_dict[menu['id']] = menu

        for k, v in m2_dict.items():
            menu_dict[k]['child'] = v
            parent_id = k
            while parent_id:
                menu_dict[parent_id]['status'] = True
                parent_id = menu_dict[parent_id]['parent_id']

        menus = []
        for k, v in menu_dict.items():
            key = v['parent_id']
            if key:
                menu_dict[key]['child'].append(v)
            else:
                menus.append(v)
        while url_parent_id:
            menu_dict[url_parent_id]['open'] = True
            url_parent_id = menu_dict[url_parent_id]['parent_id']
        return menus

    def menu_str(self):
        if self.__menu_str: return self.__menu_str
        menus = self.menu_data_list()
        if menus.count:
            s = ''
            for m in menus:
                if not m['status']: continue
                s2 = self.__menu_content_str(m['child'])
                if s2:
                    s += '<li><a href="javaScript:void(0);">%s</a>%s</li>' % (m['caption'], s2)
                elif m['url']:
                    s += '<li><a href="%s">%s</a></li>' % (m['url'], m['caption'])
                else:
                    s += '<li>%s</li>' % m['caption']
            self.__menu_str = '<ul class="active">%s</ul>' % s if s else None
            return self.__menu_str
        else:
            return None
    def __menu_content_str(self, menus):
        if menus.count:
            s = ''
            active = ''
            for m in menus:
                if not m['status']: continue
                s2 = self.__menu_content_str(m['child'])
                if s2:
                    s += '<li><a href="javaScript:void(0);">%s</a>%s</li>' % (m['caption'], s2)
                elif m['url']:
                    s += '<li><a href="%s">%s</a></li>' % (m['url'], m['caption'])
                else:
                    s += '<li>%s</li>' % m['caption']
                if m['open']:
                    active = 'active'
            return '<ul class="%s">%s</ul>' % (active, s) if s else None
        else:
            return None

    def actions(self):
        """
        检查当前用户是否对当前URL有权访问,并获取对当前URL有什么权限
        """
        action_list = []
        # 当前所有权限
        # {
        #     '/index.html': ['GET',POST,]
        # }
        for k,v in self.permission2action_dict.items():
            if re.match(k,self.current_url):
                action_list = v # ['GET',POST,]
                break
        return action_list

3、权限控制装饰器

def permission(func):
    def inner(request,*args,**kwargs):
        user_info = request.session.get('user_info')
        if not user_info:
            return redirect(reverse('login'))
        obj = MenuHelper(request, user_info['username'])
        action_list = obj.actions()
        if not action_list:
            return HttpResponse('无权限访问')
        kwargs['menu_string'] = obj.menu_str()
        kwargs['action_list'] = action_list
        return func(request,*args,**kwargs)
    return inner

4、应用

 <div class="menu">
     {{ menu_str|safe }}  # 菜单元素
 </div>
def login(request):
    if request.method == "GET":
        return render(request,'login.html')
    else:
        username = request.POST.get('username')
        pwd = request.POST.get('pwd')
        obj = models.User.objects.filter(name=username,password=pwd).first()
        if obj:
            # obj.id,  obj.username
            # 当前用户信息放置session中
            request.session['user_info'] = {'nid':obj.id,'username':obj.name}
            MenuHelper(request,obj.name)
            return redirect('/')
        else:
            return redirect('/login/')

def logout(request):
    request.session.clear()
    return redirect('/login/')

@permission
def index(request,*args,**kwargs):
    action_list = kwargs.get('action_list')
    menu_string = kwargs.get('menu_string')
    if "GET" in action_list:
        result = models.User.objects.all()
    else:
        result = []
    return render(request,'index.html',{'menu_string':menu_string,'action_list':action_list})
posted @   Bruce_JRZ  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
点击右上角即可分享
微信分享提示