zoukankan      html  css  js  c++  java
  • 权限管理

    数据库设计分析

    举个例子,一个初创公司中(CEO,产品总监,技术攻城狮,搬砖的)...寥寥几人,每个人可能会同时扮演多种角色(每种角色相对应都有一定不同的权限)

    那么,人 角色 权限三者间存在一种怎样的联系(又该怎样生成数据库表)

    So,可以确定了  用户表、角色表、权限表

    • 用户与角色是多对多的关系。
    • URL表(权限表)对应的权限的行为(功能表)是多对多的关系。
    • 现在角色可以分配相应的功能了,它们也是多对多的关系。
    • 那么还有一件事,权限应该挂载在菜单下面,所以菜单表和权限表又是一种ForeignKey的关系
    class Userinfo(models.Model):
        """
        用户表
        """
        nickname = models.CharField(max_length=32)
        password = models.CharField(max_length=32)
    
        def __str__(self):
            return self.nickname
    
    
    class Role(models.Model):
        """
        角色表
        """
        caption = models.CharField(max_length=32)
    
        def __str__(self):
            return self.caption
    
    
    class UserinfoToRole(models.Model):
        """
        用户可以扮演多种角色
        角色也可以对应多个用户
        """
        u = models.ForeignKey('Userinfo')
        r = models.ForeignKey('Role')
    
        def __str__(self):
            return '%s-%s' %(self.u.nickname, self.r.caption)
    
    
    class Url(models.Model):
        """
        权限Url表  和菜单有外键关系  应该挂载在相应的菜单下面
        """
        caption = models.CharField(max_length=32)
        url = models.CharField(max_length=32)
        menu = models.ForeignKey('Menu', null=True, blank=True)
    
        def __str__(self):
            return '%s-%s' %(self.caption, self.url)
    
    
    class Action(models.Model):
        """
        功能权限表 例如  1、增 2、删 3、改 4、查
        """
        caption = models.CharField(max_length=32)
        code = models.CharField(max_length=32)
    
        def __str__(self):
            return self.code
    
    
    class UrlToAction(models.Model):
        """
        权限分配小功能
        一个URL可能会有增 删功能,另一个可能全有
        """
        url = models.ForeignKey('Url')
        a = models.ForeignKey('Action')
    
        def __str__(self):
            return '%s-%s' %(self.url.caption, self.a.caption)
    
    
    class RoleToUrlToAction(models.Model):
        """
        角色分配权限表,功能可以对应多种角色,角色也可以对应多种功能
        """
        uTa = models.ForeignKey('UrlToAction')
        r = models.ForeignKey('Role')
    
        def __str__(self):
            return '%s-%s' %(self.r.caption, self.uTa)
    
    
    class Menu(models.Model):
        """
        菜单表
        """
        caption = models.CharField(max_length=32)
        m = models.ForeignKey('self', related_name='mTom', null=True, blank=True)
    
        def __str__(self):
            return self.caption
    权限表生成DEMO

    根据当前登陆用户获取所对应角色的权限及相应权限行为(功能)系列

    用户登陆成功之后,经一系列验证之后......

    可以通过用户名去获取当前登陆用户扮演了哪些角色,这些角色下面又有哪些权限下的所有功能???

    通过上面的表关系,,,大概可以通过四种方式可以获取当前用户所扮演的角色

        login_username = request.POST.get('user')
        # 根据登陆用户获取 用户扮演的角色
        login_user = models.Userinfo.objects.filter(nickname=login_username).first()
        # 方式一
        # role_list = models.UserinfoToRole.objects.filter(u=login_user)
        # 方式二
        # role_list = models.Role.objects.filter(userinfotorole__u=login_user)
        # 方式三
        role_list = models.Role.objects.filter(userinfotorole__u__nickname=login_username)
        # 方式四
        # 如果有多对多第三字段,通过多对多字段取
        # 前提:m = models.ManyToManyField("Role")
        # user_obj = models.User.objects.get(username=username)
        # role_list = user_obj.m.all() 
    current_loginuser角色扮演

    上面代码中,获取所扮演的角色列表role_list,接下来应该这么些角色相应的都有哪些功能(各类权限url下的功能)

        # 获取角色下所有的权限
        # 个人所有权限都将保存在session中,日后作匹配使用且无法实时更新,需重新登陆生效
        # 方式一
        # roleTourlToaction_list = models.RoleToUrlToAction.objects.filter(r__in=role_list)
        # 方式二
        # 不同角色可能相对应同样的功能,故而去重
        roleTourlToaction_list = models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).
            values('url__url', 'a__code').distinct()
    角色扮演下所有功能获取

    现在可以公开的情报:

    • 获取个人的所有权限列表,放置在session当中。可以之后在对用户Url(权限)访问进行比较。缺点:无法获取实时权限信息,需重新登陆
    • 获取到所有功能后,可以通过Url去重的方式获取用户权限(Url)
    • 且应该在菜单中显示的权限
    menu_leaf_list = models.UrlToAction.objects.
    filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True).
            values('url__id', 'url__url', 'url__caption', 'url__menu').distinct()
    获取菜单下的权限(叶子)

    接下来应该构建一些东西了,并且非常巧妙......

    A、构建权限(叶子节点)字典

        menu_leaf_dict = {}
        for item in menu_leaf_list:
            item = {
                'id': item['url__id'],
                'url': item['url__url'],
                'caption': item['url__caption'],
                'parent_id': item['url__menu'],
                'child': []
            }
            if item['parent_id'] in menu_leaf_dict:
                menu_leaf_dict[item['parent_id']].append(item)
            else:
                menu_leaf_dict[item['parent_id']] = [item, ]
            import re
            if re.match(item['url'], request.path):
                item['open'] = True
                open_leaf_parent_id = item['parent_id']
        # 此步构建了权限字典(字典的键为菜单的ID,即权限挂载在哪个菜单下)
        # 且用正则验证当前用户访问url和权限url进行匹配, 返回成功即为打开状态
        # print(menu_leaf_dict)
    巧妙之处(一)

    B、构建所有菜单字典

        # 获取所有的菜单列表(每条数据为一个字典)
        menu_list = models.Menu.objects.values('id', 'caption', 'm__id')
        menu_dict = {}
        for item in menu_list:
            item['child'] = []            # 为每个菜单设置一个孩子列表
            item['status'] = False        # 是否显示
            item['open'] = False          # 是否打开
            menu_dict[item['id']] = item  # 菜单字典赋值操作
        # 此步构建了菜单字典(键为每条菜单的id, 值为每条菜单数据并附加了一些内容)
    巧妙之处(二)

    C、将Url(权限)挂载在与之对应菜单字典上(找父亲啊找父亲),生成全新的菜单字典

        for k, v in menu_leaf_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']
        # 将已经选中的菜单标记为【展开】
        while open_leaf_parent_id:
            menu_dict[open_leaf_parent_id]['open'] = True
            open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id']
        # 此步将权限(url)挂载到了菜单的最后一层
        # 并且将权限的所有直接父级的status改为了True !妙哉
        # 再且若用户当前访问Url与权限(url)匹配,open则为打开状态 !妙哉妙哉
        # 返回了全新的菜单字典
        # print(menu_dict)
    巧妙之处(三)

    D、处理等级关系,场景应用:层级评论...

        result = []
        for row in menu_dict.values():
            if not row['parent_id']:
                # 表示为根级菜单
                result.append(row)
            else:
                # 子级菜单相应的去父菜单的child下面
                menu_dict[row['parent_id']]['child'].append(row)
        print(result)
        # 此步将所有的层级关系做了处理,形成简洁明了的树形结构
    巧妙之处(四)

    E、页面HTML显示菜单层级显示(递归实现)

        response = ''
        tpl = """
            <div class="item {0}">
                <div class="title">{1}</div>
                <div class="content">{2}</div>
            </div>
        """
        for row in result:
            # 如果状态为False,则不显示
            if not row['status']:
                continue
            active = ''
            if row['open']:
                print('ok')
                active = 'active'
            title = row['caption']
            content = menu_cotent(row['child'])
            response += tpl.format(active, title, content)
    
        return render(request, 'index.html', {'response': response})
    
    
    def menu_cotent(child_list):
        """
        递归生成html
        :param child_list: 子级列表
        :return:
        """
        response = ''
        tpl = """
            <div class="item {0}">
                <div class="title">{1}</div>
                <div class="content">{2}</div>
            </div>
        """
        for row in child_list:
            if not row['status']:  # status 
                continue
            active = ''
            if row['open']:  # open_leaf_parent_id
                active = 'active'
            if 'url' in row:
                # 如果url存在于row中, 则表示到了最终权限节点
                response += """<a href="%s" class="%s">%s</a>""" 
                            %(row['url'], active, row['caption'])
            else:
                title = row['caption']
                content = menu_cotent(row['child'])
                response += tpl.format(active, title, content)
        return response
    巧妙之处(五)

    F、优化(类之整理)

    上述代码貌似看起来很繁琐,So!下面代码将上文中数据构建及生成多级菜单封装到了类里面

    class MenuHelper(object):
        def __init__(self, request, username):
            # 当前请求的request对象
            self.request = request
            # 当前登陆的用户
            self.username = username
            # 当前访问Url
            self.current_url = request.path
            # 获取当前用户的所有权限
            self.permission2action_dict = None
            # 获取在菜单中显示的权限
            self.menu_leaf_list = None
            # 获取所有菜单
            self.menu_list = None
    
            self.session_data()
    
        def session_data(self):
            # 获取用户的所有权限信息, 作用于用户访问
            permission_dict = self.request.session.get('permission_info')
            if permission_dict:
                self.permission2action_dict = permission_dict['permission2action_dict']
                self.menu_leaf_list = permission_dict['menu_leaf_list']
                self.menu_list = permission_dict['menu_list']
            else:
                # 获取当前登陆用户所有角色
                role_list = models.Role.objects.filter(userinfotorole__u=self.username)
                # 获取角色的所有行为列表
                roleTourlToaction_list = models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).
                    values('url__url', 'a__code').distinct()
                # 构建行为字典
                roleTourlToaction_dict = {}
                for item in roleTourlToaction_list:
                    if item['url__url'] in roleTourlToaction_dict:
                        roleTourlToaction_dict[item['url__url']].append(item['a__code'])
                    else:
                        roleTourlToaction_dict[item['url__url']] = [item['a__code'], ]
    
                # 获取菜单的叶子节点, 即显示在菜单的最后一层
                menu_leaf_list = models.UrlToAction.objects. 
                    filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True). 
                    values('url__id', 'url__url', 'url__caption', 'url__menu').distinct()
                # 获取所有的菜单列表
                menu_list = models.Menu.objects.values('id', 'caption', 'parent_id')
    
                self.request.session['permission_info'] = {
                    'permission2action_dict': roleTourlToaction_dict,
                    'menu_leaf_list': menu_leaf_list,
                    'menu_list': menu_list
                }
    
                self.permission2action_dict = roleTourlToaction_dict
                self.menu_leaf_list = menu_leaf_list
                self.menu_list = menu_list
    
        def menu_data(self):
            menu_leaf_dict = {}
            open_leaf_parent_id = None
    
            # 归并所有的叶子节点
            for item in self.menu_leaf_list:
                item = {
                    'id': item['url__id'],
                    'url': item['url__url'],
                    'caption': item['url__caption'],
                    'parent_id': item['url__menu'],
                    'child': [],
                    'status': False,
                    'open': False
                }
                if item['parent_id'] in menu_leaf_dict:
                    menu_leaf_dict[item['parent_id']].append(item)
                else:
                    menu_leaf_dict[item['parent_id']] = [item, ]
                import re
                if re.match(item['url'], self.current_url):
                    item['open'] = True
                    open_leaf_parent_id = item['parent_id']
    
            # 生成菜单字典
            menu_dict = {}
            for item in self.menu_list:
                item['child'] = []
                item['status'] = False
                item['open'] = False
                menu_dict[item['id']] = item
            # 将叶子节点添加到菜单字典中...
            for k, v in menu_leaf_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']
            # 将已经选中的菜单标记为【展开】
            while open_leaf_parent_id:
                menu_dict[open_leaf_parent_id]['open'] = True
                open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id']
            # 生成树形结构数据
            result = []
            for row in menu_dict.values():
                if not row['parent_id']:
                    result.append(row)
                else:
                    menu_dict[row['parent_id']]['child'].append(row)
            return result
    
        def menu_tree(self):
            response = ''
            tpl = """
                <div class="item {0}">
                    <div class="title">{1}</div>
                    <div class="content">{2}</div>
                </div>
            """
            result = self.menu_data()
            for row in result:
                if not row['status']:
                    continue
                active = ''
                if row['open']:
                    print('ok')
                    active = 'active'
                title = row['caption']
                content = self.menu_cotent(row['child'])
                response += tpl.format(active, title, content)
            return response
    
        def menu_cotent(self, child_list):
            response = ''
            tpl = """
                    <div class="item {0}">
                        <div class="title">{1}</div>
                        <div class="content">{2}</div>
                    </div>
                """
            for row in child_list:
                if not row['status']:  # status
                    continue
                active = ''
                if row['open']:  # open_leaf_parent_id
                    active = 'active'
                if 'url' in row:
                    # 如果url存在于row中, 则表示到了最终权限节点
                    response += """<a href="%s" class="%s">%s</a>""" 
                                % (row['url'], active, row['caption'])
                else:
                    title = row['caption']
                    content = self.menu_cotent(row['child'])
                    response += tpl.format(active, title, content)
            return response
    类的封装

    权限管理简单应用

    Views

    class MenuHelper(object):
        def __init__(self, request, username, current_url):
            # 当前请求的request对象
            self.request = request
            # 当前登陆的用户
            self.username = username
            # 当前访问Url
            self.current_url = current_url
            # 获取当前用户的所有权限
            self.permission2action_dict = None
            # 获取在菜单中显示的权限
            self.menu_leaf_list = None
            # 获取所有菜单
            self.menu_list = None
    
            self.session_data()
    
        def session_data(self):
            # 获取用户的所有权限信息, 作用于用户访问
            permission_dict = self.request.session.get('permission_info')
            if permission_dict:
                self.permission2action_dict = permission_dict['permission2action_dict']
                self.menu_leaf_list = permission_dict['menu_leaf_list']
                self.menu_list = permission_dict['menu_list']
            else:
                # 获取当前登陆用户所有角色
                role_list = models.Role.objects.filter(userinfotorole__u__nickname=self.username)
                # 获取角色的所有行为列表
                roleTourlToaction_list = list(models.UrlToAction.objects.filter(roletourltoaction__r__in=role_list).
                    values('url__url', 'a__code').distinct())
                # 构建行为字典
                roleTourlToaction_dict = {}
                for item in roleTourlToaction_list:
                    if item['url__url'] in roleTourlToaction_dict:
                        roleTourlToaction_dict[item['url__url']].append(item['a__code'])
                    else:
                        roleTourlToaction_dict[item['url__url']] = [item['a__code'], ]
    
                # 获取菜单的叶子节点, 即显示在菜单的最后一层
                menu_leaf_list = list(models.UrlToAction.objects. 
                    filter(roletourltoaction__r__in=role_list).exclude(url__menu__isnull=True). 
                    values('url__id', 'url__url', 'url__caption', 'url__menu').distinct())
                # 获取所有的菜单列表
                menu_list = list(models.Menu.objects.values('id', 'caption', 'parent_id'))
    
                self.request.session['permission_info'] = {
                    'permission2action_dict': roleTourlToaction_dict,
                    'menu_leaf_list': menu_leaf_list,
                    'menu_list': menu_list
                }
    
                self.permission2action_dict = roleTourlToaction_dict
                self.menu_leaf_list = menu_leaf_list
                self.menu_list = menu_list
    
        def menu_data(self):
            menu_leaf_dict = {}
            open_leaf_parent_id = None
    
            # 归并所有的叶子节点
            for item in self.menu_leaf_list:
                item = {
                    'id': item['url__id'],
                    'url': item['url__url'],
                    'caption': item['url__caption'],
                    'parent_id': item['url__menu'],
                    'child': [],
                    'status': True,
                    'open': False
                }
                if item['parent_id'] in menu_leaf_dict:
                    menu_leaf_dict[item['parent_id']].append(item)
                else:
                    menu_leaf_dict[item['parent_id']] = [item, ]
                import re
                if re.match(item['url'], self.current_url):
                    item['open'] = True
                    open_leaf_parent_id = item['parent_id']
    
            # 生成菜单字典
            menu_dict = {}
            for item in self.menu_list:
                item['child'] = []
                item['status'] = False
                item['open'] = False
                menu_dict[item['id']] = item
            # 将叶子节点添加到菜单字典中...
            for k, v in menu_leaf_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']
            print(menu_dict)
            # 将已经选中的菜单标记为【展开】
            while open_leaf_parent_id:
                menu_dict[open_leaf_parent_id]['open'] = True
                open_leaf_parent_id = menu_dict[open_leaf_parent_id]['parent_id']
            # 生成树形结构数据
            result = []
            for row in menu_dict.values():
                if not row['parent_id']:
                    result.append(row)
                else:
                    menu_dict[row['parent_id']]['child'].append(row)
            return result
    
        def menu_tree(self):
            response = ''
            tpl = """
                <div class="item {0}">
                    <div class="title">{1}</div>
                    <div class="content">{2}</div>
                </div>
            """
            result = self.menu_data()
            for row in result:
                if not row['status']:
                    continue
                active = ''
                if row['open']:
                    active = 'active'
                title = row['caption']
                content = self.menu_cotent(row['child'])
                response += tpl.format(active, title, content)
            return response
    
        def menu_cotent(self, child_list):
            response = ''
            tpl = """
                    <div class="item {0}">
                        <div class="title">{1}</div>
                        <div class="content">{2}</div>
                    </div>
                """
            for row in child_list:
                if not row['status']:  # status
                    continue
                active = ''
                if row['open']:  # open_leaf_parent_id
                    active = 'active'
                if 'url' in row:
                    # 如果url存在于row中, 则表示到了最终权限节点
                    response += """<a href="%s" class="%s">%s</a>""" 
                                % (row['url'], active, row['caption'])
                else:
                    title = row['caption']
                    content = self.menu_cotent(row['child'])
                    response += tpl.format(active, title, content)
            return response
    
    
    def login(request):
        user_request_url = '/girl.html'
        login_user = request.GET.get('user')
        obj = MenuHelper(request, login_user, user_request_url)
        string = obj.menu_tree()
        return render(request, 'index.html',{'menu_string': string})
    Views

    Html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
            .content{
                margin-left: 20px;
                display: none;
            }
            .content a{
                display: block;
            }
            .active>.content{
                display: block;
            }
        </style>
    </head>
    <body>
        {{ menu_string | safe }}
    </body>
    </html>
    Html

    通过更改URL(相当于用户访问的权限url)从而看到显示的菜单权限

    权限管理实际应用

    更新中...

  • 相关阅读:
    j2ee学习笔记
    go开发和运行环境的配置
    Java基础学习笔记
    经典C/S服务器模型之守护进程
    linux抓包命令-tcpdump命令详解
    PostgreSQL的HA解决方案-2负载均衡(load balance)
    PostgreSQL的HA解决方案-1主从和备份(master/slave and backup)
    PostgreSQL的HA解决方案-项目概述
    将数据写到kafka的topic
    将sparkStreaming结果保存到Redshift数据库
  • 原文地址:https://www.cnblogs.com/jasonenbo/p/6730801.html
Copyright © 2011-2022 走看看