zoukankan      html  css  js  c++  java
  • 用户权限 (知识点)

    一. 权限用户表

    a. SQL表结构

    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)
                    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,)
    
    
    
    python3 manage.py startapp app02
    models.py

    b. 输出菜单

    urlpatterns = [
        url(r'^app02_test.html', views2.app02_test),
    ]
    urls.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 app02_test(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.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)
    View Code

    二. 组件

    生成公共app
           - 权限限制
           - 生成菜单
    
    python3 manage.py startapp rbac
    
    #白名单url, 不验证
    
    VALID_URL = [
        '/app01/.*',
        '/app02/.*'
        '/login.html'
        '/logout.html'
    ]
    config.py
    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)
                    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
    #验证中间件
    
    
    from django.utils.deprecation import MiddlewareMixin
    from django.shortcuts import HttpResponse
    from rbac import config
    import re
    
    
    class RbacMiddleware(MiddlewareMixin):
    
        def process_request(self,request,*args,**kwargs):
            for pattern in config.VALID_URL:
                if re.match(pattern,request.path_info):
                    return None
    
            action = request.GET.get('md') # GET
            user_permission_dict = request.session.get('user_permission_dict')
            if not user_permission_dict:
                return HttpResponse('无权限')
    
            # action_list = user_permission_dict.get(request.path_info)
            flag = False
            for k,v in user_permission_dict.items():
                if re.match(k,request.path_info):
                    if action in v:
                        flag = True
                        break
            if not flag:
                return HttpResponse('无权限')
    /middleware/md.py
    import re
    from rbac import models
    from django.utils.safestring import mark_safe
    
    def permission_session(user_id,request):
        """
    
        :param user_id:  rbac中的user表中一条数据id
        :param request:
        :return:
        """
        # obj = models.User.objects.filter(username='杨明').first()
        #
        # # x = models.User2Role.objects.filter(user_id=obj.id)
        # # [User2Role,User2Role,User2Role]
        #
        # role_list = models.Role.objects.filter(users__user_id=obj.id)
        # # [Role,]
        # from django.db.models import Count
        # # permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').annotate(c=Count('id'))
        # permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').distinct()
        """
        [
            {permission_url: '/index.html', action_code:'GET'},
            {permission_url: '/index.html', action_code:'POST'},
            {permission_url: '/index.html', action_code:'DEL'},
            {permission_url: '/index.html', action_code:'Edit'},
            {permission_url: '/order.html', action_code:'GET'},
            {permission_url: '/order.html', action_code:'POST'},
            {permission_url: '/order.html', action_code:'DEL'},
            {permission_url: '/order.html', action_code:'Edit'},
        ]
        放在Session中
        /index.html?md=GET
    
        {
            '/index.html': [GET,POST,DEL,Edit],
            '/order.html': [GET,POST,DEL,Edit],
        }
    
        """
    
        user_permission_dict = {
            '/ah-index.html': ["GET","POST","DEL","Edit"],
            '/order.html':  ["GET","POST","DEL","Edit"],
            '/index-(d+).html':  ["GET","POST","DEL","Edit"],
        }
    
        request.session['user_permission_dict'] = user_permission_dict
    
    
    def menu(user_id,current_url):
        """
        根据用户ID,当前URL:获取用户所有菜单以及权限,是否显示,是否打开
        :param user_id:
        :param current_url:
        :return:
        """
        # 所有菜单:处理成当前用关联的菜单
        all_menu_list = models.Menu.objects.all().values('id','caption','parent_id')
        user = models.User.objects.filter(id=user_id).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()
        ##### 将权限挂靠到菜单上 ########
        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
            }
            if re.match(per['permission__url'],current_url):
                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
        # ############ 处理菜单和菜单之间的等级关系 ############
        result = []
        for row in all_menu_list:
            pid = row['parent_id']
            if pid:
                all_menu_dict[pid]['child'].append(row)
            else:
                result.append(row)
    
    
        ##################### 结构化处理结果 #####################
        for row in result:
            print(row['caption'],row['status'],row['opened'],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 menu_html
    
    
    # simple_tag
    def css():
        v = """
            <style>
            .hide{
                display: none;
            }
            .menu-body{
                margin-left: 20px;
            }
            .menu-body a{
                display: block;
            }
            .menu-body a.active{
                color: red;
            }
        </style>
            """
        return v
    
    # simple_tag
    def js():
        v = """
            <script>
            $(function(){
    
                $('.menu-header').click(function(){
                    $(this).next().removeClass('hide').parent().siblings().find('.menu-body').addClass('hide');
    
                })
    
            })
        </script>
        """
        return v
    service.py

    a. 以后调用 

    #自定义的中间件加入到setting
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'rbac.middleware.md.RbacMiddleware',    
    ]
    setting.py 
    #后端
    from rbac import service
    
    1. 用户登录后,拿到用户的ID,调用permission_session()函数(函数代码还没写)
       函数获取用户角色的权限,格式如:
       “”“
        user_permission_dict = {
            '/ah-index.html': ["GET","POST","DEL","Edit"],
            '/order.html':  ["GET","POST","DEL","Edit"],
            '/index-(d+).html':  ["GET","POST","DEL","Edit"],
        }
       ”“”
    
    	def login():
    	  permission_session(用户ID,request)
    	  return .....
    
    2.setting中加入中间件,如上
      
    
    3.#获取菜单
    	current_url= request.pathinfo
    	menu_list = service.menu(用户ID,current_url)
    
    4.尽量用simple_tag
    	css = servicr.css()
    	js = servicr.js()
    
    5.前端
    
    	{{ css|safe }}
    	{{ menu_list|safe }}
    	{{ js|safe }}  

    代码  http://files.cnblogs.com/files/oyoui/rbac.zip  

    三. 项目

    teacher

    self 

  • 相关阅读:
    散列表
    5月19日
    5月17日阅读笔记3
    5月15日 阅读笔记2
    5月14日 阅读笔记1
    团队5
    团队博客2
    团队博客4
    团队博客3
    团队博客1
  • 原文地址:https://www.cnblogs.com/golangav/p/7214147.html
Copyright © 2011-2022 走看看