用户角色权限 案例

Posted karina梅梅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用户角色权限 案例相关的知识,希望对你有一定的参考价值。

#权限管理:rbac:role basic access control
# 一 根据用户获取权限, session中 中间件实现权限控制
# 二 菜单管理
#默认展开
#只显示当前用户菜单

一、权限用户表

a:SQL表结构

from django.db import models

# Create your models here.
class User(models.Model):
    """
    用户表
    """
    username = models.CharField(verbose_name=\'用户名\', max_length=32)
    password = models.CharField(verbose_name=\'密码\', max_length=64)
    email = models.EmailField(verbose_name=\'邮箱\')

    def __str__(self):
        return self.username


class Role(models.Model):
    """
    角色表
    """
    caption = models.CharField(verbose_name=\'角色\', max_length=32)

    def __str__(self):
        return self.caption


class User2Role(models.Model):
    """
    用户角色关系表
    """
    user = models.ForeignKey(User, verbose_name=\'用户\', related_name=\'roles\')
    role = models.ForeignKey(Role, verbose_name=\'角色\', related_name=\'users\')

    def __str__(self):
        return \'%s-%s\' % (self.user.username, self.role.caption,)


class Menu(models.Model):
    """
    菜单表
    """
    caption = models.CharField(verbose_name=\'菜单名称\', max_length=32)
    parent = models.ForeignKey(\'self\', verbose_name=\'父菜单\', related_name=\'p\', null=True, blank=True)

    def __str__(self):
        prev = ""
        parent = self.parent
        while True:
            if parent:
                prev = prev + \'-\' + str(parent.caption)
                parent = parent.parent
            else:
                break
        return \'%s-%s\' % (prev, self.caption,)


class Permission(models.Model):
    """
    权限
    """
    caption = models.CharField(verbose_name=\'权限\', max_length=32)
    url = models.CharField(verbose_name=\'URL正则\', max_length=128)
    menu = models.ForeignKey(Menu, verbose_name=\'所属菜单\', related_name=\'permissions\',null=True,blank=True)

    def __str__(self):
        return "%s-%s" % (self.caption, self.url,)


class Action(models.Model):
    """
    操作:增删改查
    """
    caption = models.CharField(verbose_name=\'操作标题\', max_length=32)
    code = models.CharField(verbose_name=\'方法\', max_length=32)

    def __str__(self):
        return self.caption


class Permission2Action2Role(models.Model):
    """
    权限操作关系表
    """
    permission = models.ForeignKey(Permission, verbose_name=\'权限URL\', related_name=\'actions\')
    action = models.ForeignKey(Action, verbose_name=\'操作\', related_name=\'permissions\')
    role = models.ForeignKey(Role, verbose_name=\'角色\', related_name=\'p2as\')

    class Meta:
        unique_together = (
            (\'permission\', \'action\', \'role\'),
        )

    def __str__(self):
        return "%s-%s-%s" % (self.permission, self.action, self.role,)
models.py

b.输出菜单

urlpatterns = [
    url(r\'^auth-menu.html$\',view2.menu),

]
url.py
from django.contrib import admin

# Register your models here.
from app02 import models

admin.site.register(models.User)
admin.site.register(models.Role)
admin.site.register(models.User2Role)
admin.site.register(models.Menu)
admin.site.register(models.Permission)
admin.site.register(models.Action)
admin.site.register(models.Permission2Action2Role)
app02/admin.py
def menu(request):

   """
    需要用户名或用户ID,产出:用户关联所有菜单
    :param request:
    :return:
    """

    # 所有菜单:处理成当前用关联的菜单
    all_menu_list = models.Menu.objects.all().values(\'id\', \'caption\', \'parent_id\')
    """
    [
        {\'id\':1, \'caption\':\'菜单1\', parent_id:None},
        {\'id\':2, \'caption\':\'菜单2\', parent_id:None},
        {\'id\':3, \'caption\':\'菜单3\', parent_id:None},
        {\'id\':4, \'caption\':\'菜单1-1\', parent_id:1},
    ]

    {
        1:{\'id\':1, \'caption\':\'菜单1\', parent_id:None,status:False,opened:False,child:[]},
        2:{\'id\':2, \'caption\':\'菜单2\', parent_id:None,status:False,opened:False,child:[]},
        3:{\'id\':3, \'caption\':\'菜单3\', parent_id:None,status:False,opened:False,child:[]},
        5:{\'id\':4, \'caption\':\'菜单1-1\', parent_id:1,status:False,opened:False,child:[]},
    }
   """
    user = models.User.objects.filter(username=\'alex\').first()
    role_list = models.Role.objects.filter(users__user=user)
    permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values(\'permission__id\',
                                                                                              \'permission__url\',
                                                                                              \'permission__menu_id\',
                                                                                              \'permission__caption\').distinct()



    """

    [
        {\'permission__url\':\'/order.html\',\'permission__caption\': \'订单管理\',\'permission__menu_id\': 1 },
        {\'permission__url\':\'/order.html\',\'permission__caption\': \'订单管理\',\'permission__menu_id\': 2 },
        {\'permission__url\':\'/order.html\',\'permission__caption\': \'订单管理\',\'permission__menu_id\': 3 },
        {\'permission__url\':\'/order.html\',\'permission__caption\': \'订单管理\',\'permission__menu_id\': 4 },
    ]
    """
    ##### 将权限挂靠到菜单上 ########
    all_menu_dict = {}
    for row in all_menu_list:
        row[\'child\'] = []  # 添加孩子
        row[\'status\'] = False  # 是否显示菜单
        row[\'opened\'] = False  # 是否默认打开
        all_menu_dict[row[\'id\']] = row




    for per in permission_list:
        if not per[\'permission__menu_id\']:
            continue

        item = {
            \'id\': per[\'permission__id\'],
            \'caption\': per[\'permission__caption\'],
            \'parent_id\': per[\'permission__menu_id\'],
            \'url\': per[\'permission__url\'],
            \'status\': True,
            \'opened\': False
        }

        # print(item["url"])
        if re.match(per[\'permission__url\'],request.path_info):
        # if re.match(per[\'permission__url\'], "/orders.html"):
            item[\'opened\'] = True
        pid = item[\'parent_id\']
        all_menu_dict[pid][\'child\'].append(item)

        # 将当前权限前辈status=True
        temp = pid  # 1.父亲ID
        while not all_menu_dict[temp][\'status\']:
            all_menu_dict[temp][\'status\'] = True
            temp = all_menu_dict[temp][\'parent_id\']
            if not temp:
                break

        # 将当前权限前辈opened=True
        if item[\'opened\']:
            temp1 = pid  # 1.父亲ID
            while not all_menu_dict[temp1][\'opened\']:
                all_menu_dict[temp1][\'opened\'] = True
                temp1 = all_menu_dict[temp1][\'parent_id\']
                if not temp1:
                    break
    # ############ 处理菜单和菜单之间的等级关系 ############
    """
    all_menu_dict = {
        1:{\'id\':1, \'caption\':\'菜单1\', parent_id:None,status:False,opened:False,child:[{\'permission__url\':\'/order.html\',\'permission__caption\': \'订单管理\',\'permission__menu_id\': 1 },]},
        2:{\'id\':2, \'caption\':\'菜单2\', parent_id:None,status:False,opened:False,child:[]},
        3:{\'id\':3, \'caption\':\'菜单3\', parent_id:None,status:False,opened:False,child:[]},
        5:{\'id\':4, \'caption\':\'菜单1-1\', parent_id:1,status:False,opened:False,child:[]},
    }


    all_menu_list= [
        {\'id\':1, \'caption\':\'菜单1\', parent_id:None,status:False,opened:False,child:[{\'permission__url\':\'/order.html\',\'permission__caption\': \'订单管理\',\'permission__menu_id\': 1 }, {\'id\':4, \'caption\':\'菜单1-1\', parent_id:1,status:False,opened:False,child:[]},]},
        {\'id\':2, \'caption\':\'菜单2\', parent_id:None,status:False,opened:False,child:[]},
        {\'id\':3, \'caption\':\'菜单3\', parent_id:None,status:False,opened:False,child:[]},

    ]
    """

    result = []
    for row in all_menu_list:
        pid = row[\'parent_id\']
        if pid:
            all_menu_dict[pid][\'child\'].append(row)
        else:
            result.append(row)


    ##################### 结构化处理结果 #####################
    # print(result)
    # for row in result:
    #     # print(row[\'caption\'], row[\'status\'], row[\'opened\'], )
    #     print(row)

    ##################### 通过结构化处理结果,生成菜单开始 #####################

    def menu_tree(menu_list):
        tpl1 = """
        <div class=\'menu-item\'>
            <div class=\'menu-header\'>{0}</div>
            <div class=\'menu-body {2}\'>{1}</div>
        </div>
        """
        tpl2 = """
        <a href=\'{0}\' class=\'{1}\'>{2}</a>
        """

        menu_str = ""
        for menu in menu_list:
            if not menu[\'status\']:
                continue
            # menu: 菜单,权限(url)
            if menu.get(\'url\'):
                # 权限
                menu_str += tpl2.format(menu[\'url\'],\'active\' if menu[\'opened\'] else "",menu[\'caption\'])
            else:
                # 菜单
                if menu[\'child\']:
                    child_html = menu_tree(menu[\'child\'])
                else:
                    child_html = ""
                menu_str += tpl1.format(menu[\'caption\'], child_html,"" if menu[\'opened\'] else \'hide\')

        return menu_str

    menu_html = menu_tree(result)




    return render(request, "menu_html.html", {"menu_html":menu_html})
app02/views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .menu-body{
            margin-left: 20px;
        }
        .menu-body a{
            display: block;
        }
        .menu-body a.active{
            color: red;
        }
        .hide{
            display: none;
        }
    </style>
</head>
<body>

    {{ menu_html|safe }}

    <script src="/static/jquery-3.2.1.js"></script>
    <script>
        $(function () {
            $(".menu-header").click(function () {
                $(this).next().removeClass("hide").parent().siblings().find(".menu-body").addClass("hide")
            })
        })
    </script>
</body>
</html>
menu_html

c.result 结果递归 debug调试

result =
    [
    {\'opened\': True, \'parent_id\': None, \'caption\': \'菜单一\', \'id\': 1, \'status\': True,
        \'child\': [
                {\'opened\': False, \'parent_id\': 1, \'caption\': \'帅哥管理\', \'id\': 5, \'status\': True, \'url\': \'/shuaige.html\'},
                {\'opened\': True, \'parent_id\': 1, \'caption\': \'菜单一  一\', \'id\': 4, \'status\': True,
                    \'child\': [
                        {\'opened\': True, \'parent_id\': 4, \'caption\': \'用户管理\', \'id\': 1, \'status\': True, \'url\': \'/users.html\'},
                        {\'opened\': False, \'parent_id\': 4, \'caption\': \'订单管理\', \'id\': 2, \'status\': True, \'url\': \'/orders.html\'}
                    ]
                },
                {\'opened\': False, \'parent_id\': 1, \'caption\': \'菜单一 二\', \'id\': 5, \'status\': False, \'child\': []},
                {\'opened\': False, \'parent_id\': 1, \'caption\': \'菜单一  三\', \'id\': 6, \'status\': False, \'child\': []}
            ]
    },
    {\'opened\': False, \'parent_id\': None, \'caption\': \'菜单二\', \'id\': 2, \'status\': False, \'child\': []},
    {\'opened\': False, \'parent_id\': None, \'caption\': \'菜单三\', \'id\': 3, \'status\': False, \'child\': []}
    ] 
result = [{\'opened\': True, \'parent_id\': None, \'caption\': \'菜单一\', \'id\': 1, \'status\': True, \'child\': [{\'opened\': False, \'parent_id\': 1, \'caption\': \'帅哥管理\', \'id\': 5, \'status\': True, \'url\': \'/shuaige.html\'}, {\'opened\': True, \'parent_id\': 1, \'caption\': \'菜单一  一\', \'id\': 4, \'status\': True, \'child\': [{\'opened\': True, \'parent_id\': 4, \'caption\': \'用户管理\', \'id\': 1, \'status\': True, \'url\': \'/users.html\'}, {\'opened\': False, \'parent_id\': 4, \'caption\': \'订单管理\', \'id\': 2, \'status\': True, \'url\': \'/orders.html\'}]}, {\'opened\': False, \'parent_id\': 1, \'caption\': \'菜单一 二\', \'id\': 5, \'status\': False, \'child\': []}, {\'opened\': False, \'parent_id\': 1, \'caption\': \'菜单一  三\', \'id\': 6, \'status\': False, \'child\': []}]}, {\'opened\': False, \'parent_id\': None, \'caption\': \'菜单二\', \'id\': 2, \'status\': False, \'child\': []}, {\'opened\': False, \'parent_id\': None, \'caption\': \'菜单三\', \'id\': 3, \'status\': False, \'child\': []}]


def menu_tree(menu_list):
    tpl1 = """
    <div class=\'menu-item\'>
        <div class=\'menu-header\'>{0}</div>
        <div class=\'menu-body {2}\'>{1}</div>
    </div>
    """
    tpl2 = """
    <a href=\'{0}\' class=\'{1}\'>{2}</a>
    """

    menu_str = ""
    for menu in menu_list:
        if not menu[\'status\']:
            continue
        # menu: 菜单,权限(url)
        if menu.get(\'url\'):
            # 权限
            menu_str += tpl2.format(menu[\'url\'], \'active\' if menu[\'opened\'] else "", menu[\'caption\'])
            print("***", menu_str)
        else:
            # 菜单
            if menu[\'child\']:
                child_html = menu_tree(menu[\'child\'])
                print("----", child_html)
            else:
                child_html = ""
                print("111")
            menu_str += tpl1.format(menu[\'caption\'], child_html, "" if menu[\'opened\'] else \'hide\')
            print("AAAAA", menu_str)
    print(123)
    return menu_str


menu_html = menu_tree(result)
print("xx", menu_html)

二、组件

生成公共app
       - 权限限制
       - 生成菜单
python3 manage.py startapp rbac  

配置文件:

复制代码
#白名单url, 不验证

VALID_URL = [
    \'/app01/.*\',
    \'/app02/.*\'
    \'/login.html\'
    \'/logout.html\'
]
config.py

rbac models:

from django.db import models


class User(models.Model):
    """
    用户表
    """
    username = models.CharField(verbose_name=\'用户名\', max_length=32)
    password = models.CharField(verbose_name=\'密码\', max_length=64)
    email = models.EmailField(verbose_name=\'邮箱\')

    def __str__(self):
        return self.username


class Role(models.Model):
    """
    角色表
    """
    caption = models.CharField(verbose_name=\'角色\', max_length=32)

    def __str__(self):
        return self.caption


class User2Role(models.Model):
    """
    用户角色关系表
    """
    user = models.ForeignKey(User, verbose_name=\'用户\', related_name=\'roles\')
    role = models.ForeignKey(Role, verbose_name=\'角色\', related_name=\'users\')

    def __str__(self):
        return \'%s-%s\' % (self.user.username, self.role.caption,)


class Menu(models.Model):
    """
    菜单表
    """
    caption = models.CharField(verbose_name=\'菜单名称\', max_length=32)
    parent = models.ForeignKey(\'self\', verbose_name=\'父菜单\', related_name=\'p\', null=True, blank=True)

    def __str__(self):
        prev = ""
        parent = self.parent
        while True:
            if parent:
                prev = prev + \'-\' + str(parent.caption)
    

以上是关于用户角色权限 案例的主要内容,如果未能解决你的问题,请参考以下文章

用户角色权限-数据库设计案例(转载)

Oracle-常用数据库对象笔记(片段)

RBAC从入门到精通

登录综合案例03,分配角色,分配权限,前端使用vue+elementui+axios 后端 springboot+mybatis-plus+swagger2

Sa-token给用户分配权限或分配角色

Android 逆向Linux 文件权限 ( Linux 权限简介 | 系统权限 | 用户权限 | 匿名用户权限 | 读 | 写 | 执行 | 更改组 | 更改用户 | 粘滞 )(代码片段