zoukankan      html  css  js  c++  java
  • 项目:rbac 基于角色的权限管理系统;

    - 简单示意流程图

    - RBAC分析:

      - 基于角色的权限管理;

      - 权限等于用户可以访问的URL;

      - 通过限制URL来限制权限;

    - RBAC表结构组成:

    from django.db import models
    
    
    class Menu(models.Model):
        """
        菜单表:
        """
        title = models.CharField(verbose_name='菜单名称', max_length=32, db_index=True)  # 创建索引
        icon = models.CharField(verbose_name='图标', max_length=32)
    
        def __str__(self):
            return self.title
    
        class Meta:
            # db_table = 'menu'
            verbose_name = '菜单'
            verbose_name_plural = '菜单'
    
    
    class Jurisdiction(models.Model):
        """
        权限表
        """
        url = models.CharField(max_length=32)
        title = models.CharField(verbose_name='权限名称', max_length=32)
        name = models.CharField(verbose_name='反向解析别名', max_length=32, unique=True)
        menu = models.ForeignKey(to='Menu', null=True, blank=True)
    
        def __str__(self):
            return self.title
    
        class Meta:
            verbose_name = '权限'
            verbose_name_plural = '权限'
    
    
    class Role(models.Model):
        """
        角色表
        """
        name = models.CharField(verbose_name='角色名称', max_length=32)
        permissions = models.ManyToManyField(to='Jurisdiction')
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '角色'
            verbose_name_plural = '角色'
    
    
    class User(models.Model):
        """用户表"""
        name = models.CharField(verbose_name='用户名称', max_length=32)
        password = models.CharField(verbose_name='密码', max_length=64)
        roles = models.ManyToManyField(to="Role")
    
        def __str__(self):
            return self.name
    
        class Meta:
            verbose_name = '用户'
            verbose_name_plural = '用户'
    models表结构

      - 菜单表;(用于生成二级菜单)

        - 字段:

          - id:

          - title:

          - icon:

      - 用户表;

        - 字段:

          - id:

          - name:

          - pwd:

      - 角色表;

        - 字段:

          - id:

          - name:

      - 权限表;

        - 字段:

          - id:

          - url:

          - name:

          - menu:

      - 用户与角色关联表;

        - 字段:

          - id

          - uid

          - rid

      - 角色与权限关联表;

        - 字段:

          - id

          - rid

          - pid

    - 请求访问流程:

      - 中间件:

        - 详情代码:

    import re
    
    from django.shortcuts import redirect, HttpResponse
    from django.utils.deprecation import MiddlewareMixin
    
    from django.conf import settings
    
    
    class RbacMiddleware(MiddlewareMixin):
        """
        权限校验的中间件
        """
    
        def process_request(self, request):
            """
            请求校验
            :param request:
            :return:
            """
    
            # 1. 处理白名单
            for ele in settings.VALID_LIST:
                if re.match(ele, request.path_info):
                    return None  # 通过白名单,无需再做权限校验
    
            # 2. 权限校验;去session中获取权限然后对用户请求的url 一一进行匹配。
            permission_dict = request.session.get(settings.RBAC_PERMISSION_SESSION_KEY)
            if not permission_dict:
                return redirect('/login/')
    
            flag = False
            for name, info in permission_dict.items():
                reg = "^%s$" % info['url']
                if re.match(reg, request.path_info):
                    flag = True
                    break
            if not flag:
                return HttpResponse('无权访问')
    中间件代码

        - 进行白名单设置,若访问的是白名单中的URL不做任何限制,例如访问:login/   admin/.* 等等

        - 非白名单中的URL访问的验证;

          - 获取用户浏览器中的session信息,若不存在,则让用户进行登录;

      - login视图:

        - 登录验证:

          - 代码详情:

    from django.shortcuts import render, redirect
    from rbac import models
    
    from crm.utils.md5 import gen_md5
    from rbac.service.permission import init_permission
    
    
    def login(request):
        """
        用户登录
        :param request:
        :return:
        """
        if request.method == "GET":
            return render(request, 'login.html')
    
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        pwd_md5 = gen_md5(pwd)
        # 根据用户名和密码去数据库校验,是否用户合法。
        user_object = models.User.objects.filter(name=user, password=pwd_md5).first()
    
        if not user_object:
            return render(request, 'login.html', {'msg': '用户名或密码错误'})
    
        # 用户登录成功,获取用户权限信息并放入到session中。
        # 初始化session数据
        init_permission(user_object, request)
        return redirect('/user/')
    login视图以及初始化入口

          - 获取form表单信息与数据库进行比较,验证不通过,则返回错误信息;

          - 验证通过,则进行数据初始化,并通过session写入用户浏览器中;

          - 返回重定向;

        - 数据初始化:

          - 根据用户姓名从数据库获取数据,组成相对应的数据结构,写入session;

          - 详情代码:

    from django.conf import settings
    
    
    def init_permission(user, request):
        """
        权限初始化
        :param user: 用户对象
        :param request:  请求相关信息:request.session
        :return: 无
        """
        # 1. 获取权限信息
        # user.roles.all()
        permission_queryset = user.roles.filter(permissions__id__isnull=False).values('permissions__id',
                                                                                      'permissions__title',
                                                                                      'permissions__url',
                                                                                      'permissions__name',
                                                                                      'permissions__menu_id',
                                                                                      'permissions__menu__title',
                                                                                      'permissions__menu__icon').distinct()
        # 2. 将权限和菜单信息放入到session中。设计:权限和菜单的数据结构。
        """
        权限结构 = {
            'user':{'url':'/user/'},
            'user_add':{'url':'/user/add/'},
            ...
        }
        菜单结构 = {
            菜单id:{
                title:'xxx',
                icon:'xx',
                children:[
                    {'title':'xxx','name':'xxx','url':'xxx'} # 能做菜单的权限
                ]
            }
        }
        生成的两种数据结构:
            - permission_dict: 权限认证的字典
            - menu_dict:生成二级菜单以及权限控制到按钮级别的字典
        """
        permission_dict = {}
        menu_dict = {}
        for row in permission_queryset:
            permission_dict[row["permissions__name"]] = {"url": row["permissions__url"]}
            if not row.get("permissions__menu_id"):
                continue
            if not menu_dict.get(row["permissions__menu_id"]):
                menu_dict[row["permissions__menu_id"]] = {
                    "title": row["permissions__menu__title"],
                    "icon": row["permissions__menu__icon"],
                    "children": []
                }
    
            menu_dict[row["permissions__menu_id"]]["children"].append(
                {
                    "title": row["permissions__title"],
                    "name": row["permissions__name"],
                    "url": row["permissions__url"],
                }
            )
    
        # 3. 写入session
        request.session[settings.RBAC_PERMISSION_SESSION_KEY] = permission_dict
        request.session[settings.RBAC_MENU_SESSION_KEY] = menu_dict
    初始化数据结构代码

    - 难点:

      - 表关联关系;

    - 第一版:
        - 根据用户所拥有的权限多少进行限制:
            - 需要将用户和权限进行关联:
                - 用户表
                - 权限表
            - 假若用户很多,以及权限很多;
            - 生成的关联表中的数据十分多,不利于查询;
    
    
    - 第二版:
        - 在第一版的基础上为用户创建角色:
            - 让用户表与角色进行关联;
            - 角色表与权限表进行关联;
        - 通过对不同的角色赋予不同的权限,在通过用户的角色来限制用户的权限;
            - 用户与角色需要多对多的关系,生成第三张关联表;
                - 介与用户或许会有许多不同的角色身份
                - 每个角色都会有许多用户的存在
                - 生成第三张 用户与角色关联表;
    
            - 同时 权限与角色也是需要多对多的关系:
                - 每个权限都有可能会有不同的角色都使用
                - 每个角色都有会有不同的多个权限使用;
                - 生成第四张 角色与权限关联表
    
    
    - 补充:菜单表:
        - 为了在生成二级菜单时,辅助生成的一张表;
        - 详情可见下面的菜单结构的分析

      - 数据结构搭建;

        - 两种数据结构:

    权限结构 = {
        'user':{'url':'/user/'},
        'user_add':{'url':'/user/add/'},
        ...
    }
    菜单结构 = {
        菜单id:{
            title:'xxx',
            icon:'xx',
            children:[
                {'title':'xxx','name':'xxx','url':'xxx'} # 能做菜单的权限
            ]
        }
    }

      

        - 权限结构:

          - 在权限结构中,用url的别名作为key,url的路由信息作为value,组成字典;

          - 在中间件中,读取用户携带的该权限字典中所有的values,然后循环判断即可;

          - 若想省事,可以直接组成列表;

        - 菜单结构:

          - 在所有的权限中,可以做菜单的权限不多;例如:添加,删除,更改,便不能作为菜单显示,只能作为按钮显示;

          - 需要在数据库中将可以做菜单,和不可以做菜单的表做出区别;

            - 在这里采取的做法是另外创建一张菜单表,和权限表关联,如果某条权限可以作为菜单,则将该信息和菜单表进行关联,若不可,则为None;

            - 在显示菜单的时候,根据该字段是否有值来进行判断,以便于生成菜单结构字典;

          - 若该菜单id的值存在,且在菜单结构中买有该菜单id的key则新建一个字典,该字典中所对应的 children为空列表。

          - 往所有菜单id相对应的值中的字典中的children对应的列表里添加所有的信息字典;

          - 没有菜单id的权限则continue掉;

    - 扩展点:

      - 权限控制到按钮级别;

        - 母板:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="x-ua-compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Title</title>
    </head>
    <body>
        <div style=" 20%;float: left">
            {% load rbac %}
    
            {% menu request %}
        </div>
    
        <div style=" 80%;float: left">
            {% block content %} {% endblock %}
        </div>
    
    </body>
    </html>
    简单母板

        - filter函数;

    from django.template import Library
    from django.conf import settings
    
    register = Library()
    
    
    @register.filter
    def has_permission(name, request):
        permission_dict = request.session[settings.RBAC_PERMISSION_SESSION_KEY]
    
        if name in permission_dict:
            return True
    自定义的filter

        - inclusion_tag函数;

    # rbac.py
    
    from django.template import Library
    from django.conf import settings
    
    register = Library()
    
    
    @register.inclusion_tag('menu.html')
    def menu(request):
        menu_dict = request.session[settings.RBAC_MENU_SESSION_KEY]
        return {'menu_dict': menu_dict}
    
    
    ###########################
    
    # menu.html
    <ul>
        {% for menu in menu_dict.values %}
        <li>{{ menu.title }}</li>
            <ul>
                {% for child in menu.children %}
                    <li><a href="{{ child.url }}">{{ child.title }}</a></li>
                {% endfor %}
            </ul>
        {% endfor %}
    </ul>
    自定义的inclusion

        - 继承母板以及控制到按钮级别权限的简单示例:

    {% extends 'layout.html' %}
    {% load rbac %}
    {% block content %}
        <h1>右侧内容</h1>
        {% if 'user_add'|has_permission:request %}
        <a href="">添加</a>
        {% endif %}
    
    
        <table>
            <tbody>
                {% for row in data_list %}
                    <tr>
                        <td>{{ row.name }}</td>
                        <td>{{ row.age }}</td>
                        <td>
                            {% if 'user_edit'|has_permission:request %}
                            <a href="">编辑</a>
                            {% endif %}
    
                            {% if 'user_del'|has_permission:request %}
                            <a href="">删除</a>
                            {% endif %}
                        </td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
    
    {% endblock %}
    user_list.html

      - js控制菜单hide

      - 分页组件;

      - 

  • 相关阅读:
    python 第三方库大全
    windows CMD实现的信息收集工具
    ip协议是哪一层的协议
    MetaWeblog访问地址
    通过卸载程序触发恶意进程
    IP地址查询接口
    mysql 密码忘记解决办法
    查询IP地址的免费API
    showdan
    【译】android的审计和hacking工具
  • 原文地址:https://www.cnblogs.com/Fushengliangnian/p/9931200.html
Copyright © 2011-2022 走看看