zoukankan      html  css  js  c++  java
  • 权限管理-一级菜单-二级菜单-三级菜单-路径导航和权限粒度控制到按钮级别

    权限管理 RBAC

     

    1. 权限管理

    1. 为什么要有权限?

     

    2. 开发一套权限的组件。为什么要开发组件?

     

    3. 权限是什么?

    web 开发中 URL 约等于 权限

     

    4. 表结构的设计

     

    权限表

    ID URL

    1 /customer/list/

    2 /customer/add/

     

     

    用户表

    ID name pwd

    1 ward 123

     

     

    用户和权限的关系表(多对多)

    ID user_id permission_id

    1 1 1

    1 1 2

     

    5. 写代码

    1. 查询出用户的权限写入session

    2. 读取权限信息,判断是否有权限

     

    最初版的权限管理梳理流程

    表结构

    from django.db import models


    class Permission(models.Model):
       """
      权限表
      """
       title = models.CharField(max_length=32, verbose_name='标题')
       url = models.CharField(max_length=32, verbose_name='权限')
       
       class Meta:
           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(to='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(to='Role', verbose_name='用户所拥有的角色', blank=True)
       
       def __str__(self):
           return self.name
    • settings文件配置

      • #  ###### 权限相关的配置 ######
        PERMISSION_SESSION_KEY = 'permissions'
        WHITE_URL_LIST = [
           r'^/login/$',
           r'^/logout/$',
           r'^/reg/$',
           r'^/admin/.*',
        ]
    • 其实权限就是用户能够访问那些url,不能访问那些url,我们所做的就是将每个不同身份的人

      分配不同的url

    • 在最初用户登录的时候就查询出用户的权限。并将此次权限存入到session中

      • 为什么要存入session中啊,为了不重复读取数据库,存到session中

        我们可以配置session然后将session存到缓存中(非关系型数据库中)

        这样读取的速度回很快

    • 登录成功后如何查看当前用户的权限并将其写入到session中

      • from django.shortcuts import render, HttpResponse, redirect, reverse
        from rbac import models
        from django.conf import settings

        ...

        user = models.User.objects.filter(name=username, password=pwd).first()
        # 登录成功
               # 将权限信息写入到session
               
               # 1. 查当前登录用户拥有的权限
               permission_list = user.roles.filter(permissions__url__isnull=False).values_list(
                                                                                          'permissions__url').distinct()
               # for i in permission_list:
               #     print(i)
               
               # 2. 将权限信息写入到session # 这里的键值我们做了全局配置
               request.session[settings.PERMISSION_SESSION_KEY] = list(permission_list)
               # 得到的permission_list是一个QuerySet的元组对象,因为session的存储是有数据类型限制所以转换为列表(列表中套元组)
    • 然后,该用户能够访问那些,不能访问那些,这时,我们可以将这个逻辑写在中间件这里

      • from django.utils.deprecation import MiddlewareMixin
        from django.conf import settings
        from django.shortcuts import HttpResponse
        import re


        class PermissionMiddleware(MiddlewareMixin):
           # 每一个请求来,都会走这个钩子函数
           def process_request(self, request):
               # 对权限进行校验
               # 1. 当前访问的URL
               current_url = request.path_info

               # 白名单的判断我们这里将白名单设置在了settings中,往settings中加就ok
               for i in settings.WHITE_URL_LIST:
                   if re.match(i, current_url):
                       return

               # 2. 获取当前用户的所有权限信息
               permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
               # 3. 权限的校验
               print(current_url)  # Django的session做了转换将元组转换成为一个列表
               for item in permission_list:
                   url = item[0]
                   if re.match("^{}$".format(url), current_url):
                       return
               else:
                   return HttpResponse('没有权限')

    升级版

    动态生成一级菜单

     

    表结构的设计

    from django.db import models


    class Permission(models.Model):
       """
      权限表
      """
       title = models.CharField(max_length=32, verbose_name='标题')
       url = models.CharField(max_length=32, verbose_name='权限')
    # 用来判断哪些url是菜单,哪些不是菜单
       is_menu = models.BooleanField(default=False, verbose_name='是否是菜单')
       # 记录该菜单对应的图标信息(这里是属性样式类)
       icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)

       class Meta:
           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(to='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(to='Role', verbose_name='用户所拥有的角色', blank=True)
       
       def __str__(self):
           return self.name

     

    注册层成功之后:

    user = models.User.objects.filter(name=username, password=pwd).first()
    # 将权限信息写入到session中
    init_permission(request, user)
    def init_permission(request, user):
       # 1. 查当前登录用户拥有的权限
       permission_query = user.roles.filter(permissions__url__isnull=False).values(
           'permissions__url',
           'permissions__is_menu',
           'permissions__icon',
           'permissions__title'
      ).distinct()
       print('permission_query', permission_query)
       # 存放权限信息
       permission_list = []
       # 存放菜单信息
       menu_list = []
       for item in permission_query:
           permission_list.append({'url': item['permissions__url']})
           if item.get('permissions__is_menu'):  # 如若菜单这个字段为True
               # 将这个菜单的信息先存入一个字典,然后存入session
               menu_list.append({
                   'url': item['permissions__url'],  # 权限信息
                   'icon': item['permissions__icon'],  # 图标(Bootstrap的类样式)
                   'title': item['permissions__title'],  # 标题
              })

       # 2. 将权限信息写入到session
       request.session[settings.PERMISSION_SESSION_KEY] = permission_list
       # 将菜单的信息写入到session中
       request.session[settings.MENU_SESSION_KEY] = menu_list

    母版中的菜单(一级菜单)

    在母版中合适的位置导入这个include_tag

    {% load rbac %}
    {% menu request %}

    在templatetags下的rbac.py文件中写(自定义过滤器)

    import re
    from django import template
    from django.conf import settings

    register = template.Library()


    @register.inclusion_tag('rbac/menu.html')
    def menu(request):
       menu_list = request.session.get(settings.MENU_SESSION_KEY)
       for item in menu_list:
           url = item.get('url')
           if re.match('^{}$'.format(url), request.path_info):
               item['class'] = 'active'
       return {"menu_list": menu_list}

    在templates下的rbac文件夹下创建enum.html

    <div class="static-menu">

      {% for item in menu_list %}
           <a href="{{ item.url }}" class="{{ item.class }}">
               <span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span>{{ item.title }}</a>
      {% endfor %}

    </div>
    <--这个代码的样式可以放到该app文件夹下的static下的css中建立一个menu.css-->

    因为将数据存入了session中,所以我们可以通过request.session.来获取数据

    .left-menu .menu-body .static-menu {

    }

    .left-menu .menu-body .static-menu .icon-wrap {
        20px;
       display: inline-block;
       text-align: center;
    }

    .left-menu .menu-body .static-menu a {
       text-decoration: none;
       padding: 8px 15px;
       border-bottom: 1px solid #ccc;
       color: #333;
       display: block;
       background: #efefef;
       background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
       background: -ms-linear-gradient(bottom, #efefef, #fafafa);
       background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
       background: -o-linear-gradient(bottom, #efefef, #fafafa);
       filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
       -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
       box-shadow: inset 0px 1px 1px white;
    }

    .left-menu .menu-body .static-menu a:hover {
       color: #2F72AB;
       border-left: 2px solid #2F72AB;
    }

    .left-menu .menu-body .static-menu a.active {
       color: #2F72AB;
       border-left: 2px solid #2F72AB;
    }

    settings的配置

    #  ###### 权限相关的配置 ######
    PERMISSION_SESSION_KEY = 'permissions'
    MENU_SESSION_KEY = 'menus'
    WHITE_URL_LIST = [
       r'^/login/$',
       r'^/logout/$',
       r'^/reg/$',
       r'^/admin/.*',
    ]

     

    中间件的配置

    在middlewares目录(中间件目录中)创建rbac.py文件

    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    from django.shortcuts import HttpResponse
    import re


    class PermissionMiddleware(MiddlewareMixin):
       def process_request(self, request):
           # 对权限进行校验
           # 1. 当前访问的URL
           current_url = request.path_info

           # 白名单的判断(settings中配置好了)
           for i in settings.WHITE_URL_LIST:
               if re.match(i, current_url):
                   return

           # 2. 获取当前用户的所有权限信息
           permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
           
           # 3. 权限的校验
           for item in permission_list:
               url = item['url']
               if re.match("^{}$".format(url), current_url):
                   return
           else:
               return HttpResponse('没有权限')

    应用rbac组件

     

    1、拷贝rbac组件到新的项目中并注册APP

    2、配置权限的相关信息

    #  ###### 权限相关的配置 ######
    PERMISSION_SESSION_KEY = 'permissions'
    MENU_SESSION_KEY = 'menus'
    WHITE_URL_LIST = [
       r'^/login/$',
       r'^/logout/$',
       r'^/reg/$',
       r'^/admin/.*',
    ]

    3、创建跟权限相关的表

    • 执行命令

      • python3 manage.py makemigrations

      • python3 manage.py migrate

    4、录入权限信息

    • 创建超级用户

    • 录入所有权限信息

    • 创建角色 给角色分权限

    • 创建用户 给用户分角色

    5、在登录成功之后 写入权限和菜单的信息到session中

    6、配置上中间件,进行权限的校验

    7、使用动态菜单

    <!-导入静态文件-->
    <link rel="stylesheet" href="{% static 'css/menu.css' %}">

    使用inclusion_tag
    <div class="left-menu">
       <div class="menu-body">
          {% load rbac %}
          {% menu request %}
       </div>
    </div>

     

    母版中的菜单(动态生成二级菜单)

     

    信息管理

    客户列表

    财务管理

    缴费列表

     

    User name pwd

    Role name permissions(FK) 2user

    Permission title(二) url menu(FK) 2role

    Menu title(一)

    Models.py

    from django.db import models


    class Menu(models.Model):
       """
      一级菜单
      """
       title = models.CharField(max_length=32, unique=True)  # 一级菜单的名字
       icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)

       class Meta:
           verbose_name_plural = '菜单表'
           verbose_name = '菜单表'

       def __str__(self):
           return self.title


    class Permission(models.Model):
       """
      权限表
      有关联Menu的二级菜单
      没有关联Menu的不是二级菜单,是不可以做菜单的权限
      """
       title = models.CharField(max_length=32, verbose_name='标题')
       url = models.CharField(max_length=32, verbose_name='权限')
       menu = models.ForeignKey('Menu', null=True, blank=True)

       class Meta:
           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(to='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(to='Role', verbose_name='用户所拥有的角色', blank=True)

       def __str__(self):
           return self.name

    登录

     

    from django.shortcuts import render, HttpResponse, redirect, reverse
    from rbac import models
    from django.conf import settings
    import copy
    from rbac.server.init_permission import init_permission


    def login(request):
       if request.method == 'POST':
           username = request.POST.get('username')
           pwd = request.POST.get('pwd')

           user = models.User.objects.filter(name=username, password=pwd).first()

           if not user:
               err_msg = '用户名或密码错误'
               return render(request, 'login.html', {'err_msg': err_msg})
           # 登录成功
           # 将权限信息写入到session
           init_permission(request, user)
           return redirect(reverse('customer'))
       return render(request, 'login.html')
    def init_permission(request, user):
       # 1. 查当前登录用户拥有的权限
       permission_query = user.roles.filter(permissions__url__isnull=False).values(
           'permissions__url',
           'permissions__title',
           'permissions__menu_id',
           'permissions__menu__title',
           'permissions__menu__icon',
      ).distinct()
       print(permission_query)
       # 存放权限信息
       permission_list = []
       # 存放菜单信息
       menu_dict = {}
       for item in permission_query:
           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__menu__title'],
                   'icon': item['permissions__menu__icon'],
                   'children': [
                      {
                           'title': item['permissions__title'],
                           'url': item['permissions__url']}
                  ]
              }
           else:
               menu_dict[menu_id]['children'].append(
                  {'title': item['permissions__title'], 'url': item['permissions__url']})

       # 2. 将权限信息写入到session
       request.session[settings.PERMISSION_SESSION_KEY] = permission_list
       # 将菜单的信息写入到session中
       request.session[settings.MENU_SESSION_KEY] = menu_dict

    将拿到的数据存入session

    写在一个自定义inclusion_tag

    母版

    {% load rbac %}
    {% menu request %}

    rbac.py

    import re
    from django import template
    from django.conf import settings

    register = template.Library()


    @register.inclusion_tag('rbac/menu.html')
    def menu(request):
      menu_list = request.session.get(settings.MENU_SESSION_KEY)
      return {"menu_list": menu_list}

    menu.html

    <div class="multi-menu">
      {% for item in menu_list.values %}
           <div class="item">
               <div class="title"><i class="fa {{ item.icon }}"></i> {{ item.title }}</div>
               <div class="body hide">
                  {% for child in item.children %}
                       <a href="{{ child.url }}">{{ child.title }}</a>
                  {% endfor %}
               </div>
           </div>
      {% endfor %}
    </div>

    menu.css0

    .static-menu .icon-wrap {
        20px;
       display: inline-block;
       text-align: center;
    }

    .static-menu a {
       text-decoration: none;
       padding: 8px 15px;
       border-bottom: 1px solid #ccc;
       color: #333;
       display: block;
       background: #efefef;
       background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
       background: -ms-linear-gradient(bottom, #efefef, #fafafa);
       background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%);
       background: -o-linear-gradient(bottom, #efefef, #fafafa);
       filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
       -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
       box-shadow: inset 0px 1px 1px white;
    }

    .static-menu a:hover {
       color: #2F72AB;
       border-left: 2px solid #2F72AB;
    }

    .static-menu a.active {
       color: #2F72AB;
       border-left: 2px solid #2F72AB;
    }

    .multi-menu .item {
       background-color: white;
    }

    .multi-menu .item > .title {
       padding: 10px 5px;
       border-bottom: 1px solid #dddddd;
       cursor: pointer;
       color: #333;
       display: block;
       background: #efefef;
       background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
       background: -ms-linear-gradient(bottom, #efefef, #fafafa);
       background: -o-linear-gradient(bottom, #efefef, #fafafa);
       filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
       -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
       box-shadow: inset 0 1px 1px white;
    }

    .multi-menu .item > .body {
       border-bottom: 1px solid #dddddd;
    }

    .multi-menu .item > .body a {
       display: block;
       padding: 5px 20px;
       text-decoration: none;
       border-left: 2px solid transparent;
       font-size: 13px;

    }

    .multi-menu .item > .body a:hover {
       border-left: 2px solid #2F72AB;
    }

    .multi-menu .item > .body a.active {
       border-left: 2px solid #2F72AB;
    }

     

    menu.js

    $('.item .title').click(function () {
       $(this).next().toggleClass('hide')
    })

    三级菜单

     

    此实现的是,点击菜单其他菜单关闭(js的操作)然后点击不是菜单的链接

    菜单的状态保持不变,还是展示当前状态

     

    models.py

    from django.db import models


    class Menu(models.Model):
       """
      一级菜单
      """
       title = models.CharField(max_length=32, unique=True)  # 一级菜单的名字
       icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
       weight = models.IntegerField(default=1)

       class Meta:
           verbose_name_plural = '菜单表'
           verbose_name = '菜单表'

       def __str__(self):
           return self.title


    class Permission(models.Model):
       """
      权限表
      有关联Menu的二级菜单
      没有关联Menu的不是二级菜单,是不可以做菜单的权限
      """
       title = models.CharField(max_length=32, verbose_name='标题')
       url = models.CharField(max_length=32, verbose_name='权限')
       menu = models.ForeignKey('Menu', null=True, blank=True)
       # 该权限关联的其他权限是否也是在当前url上展示
       parent = models.ForeignKey(to='Permission', null=True, blank=True)

       class Meta:
           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(to='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(to='Role', verbose_name='用户所拥有的角色', blank=True)

       def __str__(self):
           return self.name

    登录

    from django.shortcuts import render, HttpResponse, redirect, reverse
    from rbac import models
    from django.conf import settings
    import copy
    from rbac.server.init_permission import init_permission


    def login(request):
       if request.method == 'POST':
           username = request.POST.get('username')
           pwd = request.POST.get('pwd')

           user = models.User.objects.filter(name=username, password=pwd).first()

           if not user:
               err_msg = '用户名或密码错误'
               return render(request, 'login.html', {'err_msg': err_msg})

           # 登录成功
           # 将权限信息写入到session
           init_permission(request, user)

           return redirect(reverse('customer'))

       return render(request, 'login.html')

    当中间件校验通过之后init_permission.py将权限写入到session

    from django.conf import settings

    def init_permission(request, user):
       # 1. 查当前登录用户拥有的权限
       permission_query = user.roles.filter(permissions__url__isnull=False).values(
           'permissions__url',
           'permissions__title',
           'permissions__id',
           'permissions__parent_id',
           'permissions__menu_id',
           'permissions__menu__title',
           'permissions__menu__icon',
           'permissions__menu__weight',  # 带单排序用的
      ).distinct()
       print(permission_query)
       # 存放权限信息
       permission_list = []
       # 存放菜单信息
       menu_dict = {}
       for item in permission_query:
           permission_list.append({'url': item['permissions__url'],
                                   'id': item['permissions__id'],
                                   'parent_id': item['permissions__parent_id'], })
           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__menu__title'],
                   'icon': item['permissions__menu__icon'],
                   'weight': item['permissions__menu__weight'],
                   'children': [
                      {
                           'title': item['permissions__title'],
                           'url': item['permissions__url'],
                           'id': item['permissions__id'],
                           'parent_id': item['permissions__parent_id'],
                      }
                  ]
              }
           else:
               menu_dict[menu_id]['children'].append(
                  {
                       'title': item['permissions__title'],
                       'url': item['permissions__url'],
                       'id': item['permissions__id'],
                       'parent_id': item['permissions__parent_id'],
                  })

       # 2. 将权限信息写入到session
       request.session[settings.PERMISSION_SESSION_KEY] = permission_list
       # 将菜单的信息写入到session中
       request.session[settings.MENU_SESSION_KEY] = menu_dict

     

    中间件

    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    from django.shortcuts import HttpResponse
    import re


    class PermissionMiddleware(MiddlewareMixin):
       def process_request(self, request):
           # 对权限进行校验
           # 1. 当前访问的URL
           current_url = request.path_info

           # 白名单的判断
           for i in settings.WHITE_URL_LIST:
               if re.match(i, current_url):
                   return

           # 2. 获取当前用户的所有权限信息
           permission_list = request.session.get(settings.PERMISSION_SESSION_KEY)
         
      # 3. 权限的校验
           for item in permission_list:
               url = item['url']
               if re.match("^{}$".format(url), current_url):
                   parent_id = item['parent_id']
                   id = item['id']
                   if parent_id:
                       # 表示当前权限是子权限,让父权限是展开
                       request.current_menu_id = parent_id
                   else:
                       # 表示当前权限是父权限,要展开的二级菜单
                       request.current_menu_id = id
                   return
           else:
               return HttpResponse('没有权限')

    templatetags的rbac.py的inclusion_tag

    import re
    from collections import OrderedDict
    from django import template
    from django.conf import settings

    register = template.Library()

    @register.inclusion_tag('rbac/menu.html')
    def menu(request):
       menu_list = request.session.get(settings.MENU_SESSION_KEY)
       order_dict = OrderedDict()  # 创建一个有序的字典,为输出的菜单属性做准备

       for key in sorted(menu_list, key=lambda x: menu_list[x]['weight'], reverse=True):
           order_dict[key] = menu_list[key]
           item = order_dict[key]
           item['class'] = 'hide'  # 一级菜单加类属性
           for i in item['children']:
               # 相当于事件的委托,如果是菜单就展示菜单,如果不是菜单,就委托给菜单展示页面
               if i['id'] == request.current_menu_id:
                   i['class'] = 'active'  # 二级菜单加类属性
                   item['class'] = ''  # 如果二级菜单是展开的将隐藏属性去掉
       return {"menu_list": order_dict}

    menu.html

    <div class="multi-menu">
       <!-- 循环展示一级菜单 -->
      {% for item in menu_list.values %}
           <div class="item">
               <div class="title"><i class="fa {{ item.icon }}"></i> {{ item.title }}</div>
               <div class="body {{ item.class }}">
                   <!-- 循环展示二级菜单,设置该按钮为被选中 -->
                  {% for child in item.children %}
                       <a href="{{ child.url }}" class="{{ child.class }}">{{ child.title }}</a>
                  {% endfor %}
               </div>
           </div>
      {% endfor %}
    </div>

    路径导航和权限粒度控制到按钮级别

     

    表结构

    models.py

    from django.db import models


    class Menu(models.Model):
       """
      一级菜单
      """
       title = models.CharField(max_length=32, unique=True)  # 一级菜单的名字
       icon = models.CharField(max_length=32, verbose_name='图标', null=True, blank=True)
       # 用来记录菜单的展现先后顺序
       weight = models.IntegerField(default=1)

       class Meta:
           verbose_name_plural = '菜单表'
           verbose_name = '菜单表'

       def __str__(self):
           return self.title


    class Permission(models.Model):
       """
      权限表
      有关联Menu的二级菜单
      没有关联Menu的不是二级菜单,是不可以做菜单的权限
      """
       title = models.CharField(max_length=32, verbose_name='标题')
       url = models.CharField(max_length=32, verbose_name='权限')
       menu = models.ForeignKey('Menu', null=True, blank=True)
       # 该权限关联的其他权限是否也是在当前url上展示
       parent = models.ForeignKey(to='Permission', null=True, blank=True)
    # 用来记录次url对应的名字 eg: web:customer
       name = models.CharField(max_length=32, null=True, blank=True, unique=True)

       class Meta:
           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(to='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(to='Role', verbose_name='用户所拥有的角色', blank=True)

       def __str__(self):
           return self.name

    登录:

    from django.shortcuts import render, HttpResponse, redirect, reverse
    from rbac import models
    from django.conf import settings
    import copy
    from rbac.server.init_permission import init_permission


    def login(request):
       if request.method == 'POST':
           username = request.POST.get('username')
           pwd = request.POST.get('pwd')

           user = models.User.objects.filter(name=username, password=pwd).first()

           if not user:
               err_msg = '用户名或密码错误'
               return render(request, 'login.html', {'err_msg': err_msg})

           # 登录成功
           # 将权限信息写入到session
           init_permission(request, user)
           return redirect(reverse('web:customer'))

       return render(request, 'login.html')

    登录成功后将权限信息写入到session中init_permission.py文件

    from django.conf import settings
    def init_permission(request, user):
       # 1. 查当前登录用户拥有的权限
       permission_query = user.roles.filter(permissions__url__isnull=False).values(
           'permissions__url',  # 当前用户的权限信息
           'permissions__title',  # 当前用户的权限信息的标题
           'permissions__id',  # 当前用户的id
           'permissions__name',  # 当前用户权限对应的名字
           'permissions__parent_id',  # 权限的外键
           'permissions__parent__name',  # 该权限关联的权限的名字
           'permissions__menu_id',
           'permissions__menu__title',
           'permissions__menu__icon',
           'permissions__menu__weight',  # 带单排序用的
      ).distinct()
       print(permission_query)
       # 存放权限信息
       permission_dict = {}
       # 存放菜单信息
       menu_dict = {}
       for item in permission_query:  # 遍历该用户所有的权限
           # 存放权限信息,以字典的形式,键为权限的名字
           # 将每一个权限的信息已字典的信息存起来,这些信息包括
           # 权限、权限id、该权限的父权限的id、name、权限的标题
           permission_dict[item['permissions__name']] = ({
               'url': item['permissions__url'],
               'id': item['permissions__id'],
               'parent_id': item['permissions__parent_id'],
               'parent_name': item['permissions__parent__name'],
               'title': item['permissions__title'],
          })
           # 获取每一个权限的菜单id
           menu_id = item.get('permissions__menu_id')
           # 如果没有menu_id则没有菜单
           if not menu_id:
               continue
           # 如果有menu_id则有菜单
           if menu_id not in menu_dict:
               # 如果这一个权限有菜单,那么将这个权限的菜单信息存下来
               # 菜单的标题、菜单的图标、菜单的权重、还有就是子菜单信息
               menu_dict[menu_id] = {
                   'title': item['permissions__menu__title'],
                   'icon': item['permissions__menu__icon'],
                   'weight': item['permissions__menu__weight'],
                   # 子菜单存,标题、访问权限、id、和他的父权限
                   'children': [
                      {
                           'title': item['permissions__title'],
                           'url': item['permissions__url'],
                           'id': item['permissions__id'],
                           'parent_id': item['permissions__parent_id'],
                      }
                  ]
              }
           else:
               # 如果键相同只需要将子菜单存起来就行
               menu_dict[menu_id]['children'].append(
                  {
                       'title': item['permissions__title'],
                       'url': item['permissions__url'],
                       'id': item['permissions__id'],
                       'parent_id': item['permissions__parent_id'],
                  })

       # 2. 将权限信息写入到session
       request.session[settings.PERMISSION_SESSION_KEY] = permission_dict
       # 将菜单的信息写入到session中
       request.session[settings.MENU_SESSION_KEY] = menu_dict

     

    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    from django.shortcuts import HttpResponse
    import re


    class PermissionMiddleware(MiddlewareMixin):
       def process_request(self, request):
           # 对权限进行校验
           # 1. 当前访问的URL
           current_url = request.path_info

           # 白名单的判断
           for i in settings.WHITE_URL_LIST:
               if re.match(i, current_url):
                   return

           # 2. 获取当前用户的所有权限信息
           permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY)

           # 路劲导航
           request.breadcrumd_list = [
              {"title": '首页', 'url': '#'},
          ]

           # 3. 权限的校验
           print(current_url)

           # 从session中拿出当前用户的权限信息,遍历每个权限,与当前访问的权限做比较
           # 在权限列表中的就可以访问,没有权限的就没有权限
           for item in permission_dict.values():
               url = item['url']  # 当前这个权限的url
               # 如果这个权限url与当前访问的url匹配上了
               if re.match("^{}$".format(url), current_url):
                   # 拿到这个权限的父权限的id、本身的id、父权限的名字
                   parent_id = item['parent_id']
                   id = item['id']
                   parent_name = item['parent_name']
                   # 如果存在父权限
                   if parent_id:
                       # 表示当前权限是子权限,让父权限是展开
                       request.current_menu_id = parent_id
                       # 添加面包屑导航
                       # 如果当前权限是自权限,就应该先将父权限的内容加进来
                       request.breadcrumd_list.extend([
                          {"title": permission_dict[parent_name]['title'],
                            'url': permission_dict[parent_name]['url']},
                          {"title": item['title'], 'url': item['url']},
                      ])
                   else:
                       # 表示当前权限是父权限,要展开的二级菜单
                       request.current_menu_id = id
                       # 添加面包屑导航
                       # 如果当前权限是父权限直接将当前导航的内容存起来就行
                       request.breadcrumd_list.append({"title": item['title'], 'url': item['url']})
                   return
           else:
               return HttpResponse('没有权限')

    templatetags中的rbac.py

    import re
    from collections import OrderedDict
    from django import template
    from django.conf import settings

    register = template.Library()


    # 用来展示菜单
    @register.inclusion_tag('rbac/menu.html')
    def menu(request):
       menu_list = request.session.get(settings.MENU_SESSION_KEY)
       order_dict = OrderedDict()

       for key in sorted(menu_list, key=lambda x: menu_list[x]['weight'], reverse=True):
           order_dict[key] = menu_list[key]
           item = order_dict[key]
           item['class'] = 'hide'

           for i in item['children']:

               if i['id'] == request.current_menu_id:
                   i['class'] = 'active'
                   item['class'] = ''
       return {"menu_list": order_dict}


    # 用来控制路径导航
    @register.inclusion_tag('rbac/ssssss.html')
    def breadcrumb(request):
       return {"breadcrumd_list": request.breadcrumd_list}

    # 用来控制 权限力度控制到按钮级别
    @register.filter
    def has_permission(request, permission):
       if permission in request.session.get(settings.PERMISSION_SESSION_KEY):
           return True

    menu.html

    <div class="multi-menu">
      {% for item in menu_list.values %}
           <div class="item">
               <div class="title"><i class="fa {{ item.icon }}"></i> {{ item.title }}</div>
               <div class="body {{ item.class }}">
                  {% for child in item.children %}
                       <a href="{{ child.url }}" class="{{ child.class }}">{{ child.title }}</a>
                  {% endfor %}
               </div>
           </div>
      {% endfor %}
    </div>

    ssssss.html

    <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">

       <!-- 这个判断就是为了让最后一个导航不能有链接 -->
      {% for li in breadcrumd_list %}
          {% if forloop.last %}
           <li>{{ li.title }}</li>
          {% else %}
           <li><a href="{{ li.url }}">{{ li.title }}</a></li>
          {% endif %}
      {% endfor %}


    </ol>

    layout.html

    <!--用来生成菜单与inclution_tag配合-->
    {% load rbac %}
    {% menu request %}

    <!--用来生成导航栏与inclution_tag配合-->
    {% breadcrumb request %}

    customer_list.html用来展示

    通过权限的名字判断来按钮的展示(与路由分发的include的namespace和方向解析的name配合使用)

    {% extends 'layout.html' %}

    {% block content %}
      {% load rbac %}

       <div class="luffy-container">
           <div class="btn-group" style="margin: 5px 0">
               <!--判断当前权限是否拥有-->
              {% if request|has_permission:'web:customer_add' %}
                   <a class="btn btn-default" href="{% url 'web:customer_add' %}">
                       <i class="fa fa-plus-square" aria-hidden="true"></i> 添加客户
                   </a>
              {% endif %}

           </div>
           <table class="table table-bordered table-hover">
               <thead>
               <tr>
                   <th>ID</th>
                   <th>客户姓名</th>
                   <th>年龄</th>
                   <th>邮箱</th>
                   <th>公司</th>
                  {% if request|has_permission:'web:customer_edit' or request|has_permission:'web:customer_del' %}
                       <th>选项</th>
                  {% endif %}
               </tr>
               </thead>
               <tbody>
              {% for row in data_list %}
                   <tr>
                       <td>{{ row.id }}</td>
                       <td>{{ row.name }}</td>
                       <td>{{ row.age }}</td>
                       <td>{{ row.email }}</td>
                       <td>{{ row.company }}</td>
                      {% if request|has_permission:'web:customer_edit' or request|has_permission:'web:customer_del' %}
                       <td>
                      {% if request|has_permission:'web:customer_edit' %}
                           <a style="color: #333333;" href="{% url 'web:customer_edit' row.id %}">
                               <i class="fa fa-edit" aria-hidden="true"></i></a>
                          {% endif %}
                      {% if request|has_permission:'web:customer_del' %}
                           <a style="color: #d9534f;" href="{% url 'web:customer_del' row.id %}"><i class="fa fa-trash-o"></i></a>
                      {% endif %}
                       </td>
                      {% endif %}
                   </tr>
              {% endfor %}
               </tbody>
           </table>
       </div>
    {% endblock %}

     

  • 相关阅读:
    Java IO 流 一些散乱的笔记
    Java IO 流
    Java 枚举
    Java 内部类种类及使用解析
    Java 事件适配器 Adapter
    Java 异常基础 Exception
    Java中内置的观察者模式
    Java 代理模式(一) 静态代理
    Java 可变参数
    Java中的反射机制(二) 一个利用反射进行对象拷贝的例子
  • 原文地址:https://www.cnblogs.com/xiao-xue-di/p/9885998.html
Copyright © 2011-2022 走看看