zoukankan      html  css  js  c++  java
  • 权限管理-设计分析以及具体细节

    说起权限我们大家都知道,不一样的角色会有不一样的权限。比如就像学生管理系统一样,管理员,老师,学生之间的权限都是不一样的,那么展示的页面也是不一样的。所以,我们现在来看看具体操作。

    目标:生成一个独立的组件,到哪都能用

    一、先创建一个 项目,建一个app01和rbac的应用

    二、表结构设计

     1、先看配置文件合适不,给创建的rbac在配置文件里面设置一下
        找到INSTALLED_APPS=['rbac']

    配置静态文件

    2、设计表结构
        models中创建类:五个类,七张表
        角色表:
        用户表:
        权限表:

        权限组表:

        菜单表: 

        角色表和权限表是多对多的关系(一个角色可以有多个权限,一个权限可以对应多个角色)
        用户表和角色表是多对多的关系(一个用户可以有多个角色,一个角色有多个用户)

        所以有会多生成两张关系表

        一个菜单下面有多个组

        一个组下面有多个菜单

        一个菜单下面有多个权限

     

    from django.db import models
    
    # Create your models here.
    class Role(models.Model):
        title = models.CharField(max_length=32,verbose_name="角色")
        permissions = models.ManyToManyField(to="Permission",verbose_name="拥有权限的角色",blank=True)  #权限和角色是多对多的关系
    
        def __str__(self):
            return self.title
        class Meta:
            verbose_name_plural = "角色表"
    
    class Permission(models.Model):
        title = models.CharField(max_length=32,verbose_name="权限名")
        url = models.CharField(max_length=32,verbose_name="带正则的url")
        codes = models.CharField(max_length=32,verbose_name="代码")
        group = models.ForeignKey(to="Group",verbose_name="所属组",blank=True)  #组和权限是一对多的关系,一个组有多个权限
        menu_gp = models.ForeignKey(to='Permission',related_name='aaa',null=True,blank=True,verbose_name="组内菜单")
        def __str__(self):
            return self.title
        class Meta:
            verbose_name_plural = "权限表"
    
    class UserInfo(models.Model):
        name = models.CharField(max_length=32,verbose_name="姓名")
        password = models.CharField(max_length=64,verbose_name="密码")
        email = models.CharField(max_length=32,verbose_name="邮箱")
        roles = models.ManyToManyField(to="Role",blank=True)  #用户和角色是多对多的关系
        def __str__(self):
            return self.name
        class Meta:
            verbose_name_plural = "用户表"
    
    class Group(models.Model):
        title = models.CharField(max_length=32,verbose_name="组名称")
        menu = models.ForeignKey(to="Menu",verbose_name="组内菜单",blank=True)  #一个组下有多个菜单
        def __str__(self):
            return self.title
        class Meta:
            verbose_name_plural = "权限"
    
    class Menu(models.Model):
        caption = models.CharField(max_length=32,verbose_name="菜单")
        def __str__(self):
            return self.caption
        class Meta:
            verbose_name_plural = "菜单表"

    具体分析为什么要多加个code列和权限组表呢?

    1、我们一般是先看到的是列表页面,在这个页面上是否显示添加,是否显示编辑,是否显示删除,都是需要判断的
       有无添加权限,有无删除权限,有无编辑权限,我们可以给每一个url一个代号

    dict = {
        1:{                    代号
              /userinfo/            list
           /userinfo/add/       add
           /userinfo/del(d+)/    del 
           /userinfo/edit(d+)/    edit
        }
      }
             

    不仅在列表页面需要知道他有那些权限,在其他页面也知道他有那些权限
    所以上面的方案还是有点不好,那么我们采取下面的方案。将代号取出来放在一个列表里面

    dict = {
          1:{
                  "codes":["list","add","del","edit"]
                 urls:[
                    "/userinfo/",
                    "/userinfo/add"/,
                    "/userinfo/del(d+)/ ",
                    "/userinfo/edit(d+)/ ",
                  ]    
            }
          2:{
               "codes":{"list","add","del","edit"}
                urls:[
                     "/order",
                     "/order/add"/,
                      "/order/del(d+)/ ",
                     "/order/edit(d+)/ ",
                   ]    
           }
    }            

    把这个字典存到session中
    当你访问页面的时候我就知道你有什么权限
    一个url对应一个code
    多个url对应一个组

    注意:
      关联字段 null = True        数据库用的时候可以为空
      关联字段 blank = True     admin用的时候可以为空
    当出现这个错误的时候

    解决办法

    python manage.py migrate --fake 废弃

    三、通过django-admin录入权限数据

    - 先创建一个超级用户 python3 manage.py createsuperuser
        - 用户名 root
        - 密码 zhy123456
        - 在admin.py 中
            from rbac import models
            admin.site.register(models.Permission)
            admin.site.register(models.Role)
            admin.site.register(models.UserInfo)
          这样的话上去的是英文的,如果你想让中文显示就在类中加一个类
            class Meta:
               verbose_name_plural = "权限表"
          - 当你给关联字段录入数据的时候会有错误提示,那么在类中你的那个关联字段在加一个属性blank = True 可以为空
          permissions = models.ManyToManyField(to="Permission",verbose_name="具有的所有权限", blank=True)

    四、编写登录

    1.编写登录
    2.如果用户验证成功就设置session
    3.先查出当前用户的所有的权限
    4.从这些权限中找到所有的url,吧这些url放到session中
       这些都是在rbac里面的操作,如果我们做一些复杂的操作,可能会有好多的代码
     我们写rbac的目的是做成一个公共的组件,为了让别人省事
     我们在创建一个server的文件夹,里面创建一个init_permission的py文件。

     结构化数据:方便以后做操作。。。

    dict = {
          1:{
                  "codes":["list","add","del","edit"]
                 urls:[
                    "/userinfo/",
                    "/userinfo/add"/,
                    "/userinfo/del(d+)/ ",
                    "/userinfo/edit(d+)/ ",
                  ]    
            }
          2:{
               "codes":{"list","add","del","edit"}
                urls:[
                     "/order",
                     "/order/add"/,
                      "/order/del(d+)/ ",
                     "/order/edit(d+)/ ",
                   ]    
           }
    }            

    5.拿到用户请求的url去session里面做验证
      获取当前请求的url
      获取session中保存当前用户的权限
      然后开始验证
      如果匹配成功就有权访问
      如果匹配不成功就无权访问
      用re去匹配的时候,re.match(/userinfo/,/userinfo/add) #都能匹配到
      那么要记得在匹配正则的时候加个起始符和终止符regex = "^{0}$".format(url)
      def login(request):
        .....
        设置session
      def index(request):
        ....
        获取session
      def userinfo(request):
        获取session
    这样如果有好多个函数,就的重复好多代码,我们可以用中间件来处理
    中间件和装饰器的区别:
      中间件用来做批量处理
      如果函数不多的话可以用加装饰器的方法

    五、中间件:获取session,并且当用户匹配成功的时候,先把code保存在request中,方便以后判断

    1、记得要配置白名单

    2、必须继承MiddlewareMixin这个类

    class MiddlewareMixin(object):
        def __init__(self, get_response=None):
            self.get_response = get_response
            super(MiddlewareMixin, self).__init__()
    
        def __call__(self, request):
            response = None
            if hasattr(self, 'process_request'):
                response = self.process_request(request)
            if not response:
                response = self.get_response(request)
            if hasattr(self, 'process_response'):
                response = self.process_response(request, response)
            return response

    六、设计权限管理-----问题:在访问列表页面时,是否需要判断有无添加权限、有无删除权限、有无编辑权限。 

    views

    def userinfo(request):
        # 方式一
        # Page_permission = request.permission_code_list
        # 方式二:实例化
        page_permission = BasePagePermission(request.permission_code_list)
        print("page_permission",request.permission_code_list)
        data_list = [
            {"id":1,"name":"xxx1"},
            {"id":2,"name":"xxx2"},
            {"id":3,"name":"xxx3"},
            {"id":4,"name":"xxx4"},
            {"id":5,"name":"xxx5"},
        ]
        return render(request,"userinfo.html",{"data_list":data_list,"page_permission":page_permission})
    
        

    在模板userinfo.html中:两种使用方式

    方式一:

    <table>
        {% if "add" in Page_permission %}
            <a href="#">添加</a>
        {% endif %}
        {% for row in data_list %}
             <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.name }}</td>
                 {% if "edit" in Page_permission %}
                    <td><a href="#">编辑</a></td>
                 {% endif %}
               {% if "del" in Page_permission %}
                     <td>{<a href="#">删除</a></td>
               {% endif %}
            </tr>
        {% endfor %}
    </table>

    如果不想像上面一样每个都判断,那么还有第二种方法,

    方式二:

    吧permission_code_list处理一下
    在views中定义一个类
    class BasePagePermission(object):
        def __init__(self,code_list):
            self.code_list = code_list
        def has_add(self):
            if "add" in self.code_list:
                return True
        def has_del(self):
            if "del" in self.code_list:
                return True
        def has_edit(self):
            if "edit" in self.code_list:
                return True
    实例化:page_permission = BasePagePermission(request.permission_code_list)
    在模板中
    <table>
        {% if page_permission.has_add %}
            <a href="#">添加</a>
        {% endif %}
        {% for row in data_list %}
             <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.name }}</td>
                 {% if page_permission.has_edit %}
                    <td><a href="#">编辑</a></td>
                 {% endif %}
               {% if page_permission.has_del %}
                     <td>{<a href="#">删除</a></td>
               {% endif %}
            </tr>
        {% endfor %}
    </table>

    七、设计菜单管理-----问题:1、如何生成菜单

                     2、怎么让这些菜单分级显示并且如果当前访问的url权限默认展开如果是组内菜单就加粗或者变红

                  3、非菜单url,默认选中原菜单。(如果你是点击用户列表进来的,那么你看到页面了,如果你点击添加的时候,你的那个用户列看不见了,这就不好了。所以要设计当你点击添加按钮的时候,那个用户列表被默认选中)

    菜单管理
      菜单一
        用户管理
        权限管理
      菜单二
        订单管理
        角色管理

    分级做了菜单。这些菜单该显示什么菜单?是当前用户登录之后从数据库拿到这个用户拥有的权限,然后把权限搞成菜单

    在表里面设计了一个组内菜单(自关联 ),当menu_gp_id为NULL就代表可以作为菜单

    1、在初始化的时候,初始化权限信息,获取权限信息并放置到session中

     menu_list = []
        for item in permission_list:
            tpl = {
                "id":item["permissions__id"],
                "title":item["permissions__title"],
                "url":item["permissions__url"],
                "menu_gp_id":item["permissions__menu_gp_id"],
                "menu_id":item["permissions__group__menu_id"],
                "menu_title":item["permissions__group__menu__caption"]
            }
            menu_list.append(tpl)
        request.session[settings.PERMISSION_MENU_KEY] = menu_list

    因为是要在页面上渲染,一般我们会在视图函数的render里面加{"":变量}这样渲染,
    但是还有个更好用的方法:用自定义的标签

    具体操作:
      1、找到app创建一个templatetags的文件夹
      2、然后在里面随便创建一个文件
      3、导入form django.template import Library
           register = Library()

          方式一:
            @register.simple_tag
            def menu():
              return "菜单"     这里返回啥页面上就显示啥
            然后在母版里面导入mnue.html
            {% load rbac %}

          方式二:
            @register.includsion_tag("xxx.html") #这里存放的是html文件,,,@register.includsion_tag("xxx.html")   自动会读这个文件并且把返回值拿到在页面上渲染
            def menu():
              return "菜单"     这里返回啥页面上就显示啥
            “在母版中:{%menu_html  request%}     request是参数,记得要加上{% load rbac %}

      4、注意:
            如果有两个文件夹同名,避免发生冲突:就再创建一个文件夹包起来

     2、去Session中获取菜单相关信息,匹配当前URL,生成菜单

    先把和菜单相关的所有字段取出来

    menu_list = [
        {'id': 1, 'title': '用户列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜单二'}, 
        {'id': 2, 'title': '添加用户', 'url': '/userinfo/add/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜单二'}, 
        {'id': 3, 'title': '删除用户', 'url': '/userinfo/del/(\d+)/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜单二'}, 
        {'id': 4, 'title': '编辑用户', 'url': '/userinfo/edit/(\d+)/', 'menu_gp_id': 1, 'menu_id': 2, 'menu_title': '菜单二'}, 
        {'id': 5, 'title': '订单列表','url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜单一'}, 
        {'id': 6, 'title': '添加订单', 'url': '/order/add/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜单一'}, 
        {'id': 7, 'title': '删除订单', 'url': '/order/del/(\d+)/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜单一'}, 
        {'id': 8, 'title': '编辑订单', 'url': '/order/edit/(\d+)/', 'menu_gp_id': 2, 'menu_id': 1, 'menu_title': '菜单一'}
    ]

    然后循环列表找出可以作为菜单的权限

    {
        1: {'id': 1, 'title': '用户列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜单二'}, 
        5: {'id': 5, 'title': '订单列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜单一'}
    }

    再次循环列表向上边的字典中添加active

    {
        1: {'id': 1, 'title': '用户列表', 'url': '/userinfo/', 'menu_gp_id': None, 'menu_id': 2, 'menu_title': '菜单二', 'active': True},
        5: {'id': 5, 'title': '订单列表', 'url': '/order/', 'menu_gp_id': None, 'menu_id': 1, 'menu_title': '菜单一'}
    }

    结构化数据(吧上面得到的数据化成下面这样格式的,方便以后使用)

    {
        1: {
                'menu_id': 1,
                'menu_title': '菜单一',
                'active': None, 
                'children': [
                        {'title': '订单列表', 'url': '/order/', 'active': None}
                    ]
                }
        2: {
            'menu_id': 2, 
            'menu_title': '菜单二', 
            'active': True,
            'children': [
                    {'title': '用户列表', 'url': '/userinfo/', 'active': True}
                ]
            },
        
    }
  • 相关阅读:
    298. Binary Tree Longest Consecutive Sequence
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    163. Missing Ranges
    336. Palindrome Pairs
    727. Minimum Window Subsequence
    211. Add and Search Word
    年底购物狂欢,移动支付安全不容忽视
    成为程序员前需要做的10件事
    全球首推iOS应用防破解技术!
  • 原文地址:https://www.cnblogs.com/morgana/p/8496388.html
Copyright © 2011-2022 走看看