• day24-基于rbac的crm


    今日内容:不同用户分权限(展示不同的菜单)
    内容回顾:
    settings的配置文件中的key都是大写
    内容回顾:
        1.使用别人源码,启动:解释器+工作目录
        
        2. django请求生命周期
        
        3. 配置文件
            - key必须大写
            - 导入配置
                from django.conf import settings
        
        3. 在模板中定义函数
            - sample_tag
            - inclusion_tag
        
        4. 寻找模板的顺序(静态文件)
            - 最外层templates目录 (static- 去注册的app下的templates目录中找(按照app注册顺序)(static5. auto示例:所有用户登录后看到的菜单都一样。
    回顾

    今日内容:
    1.回顾用户、角色、权限表的创建
    2.设计含菜单的最终表结构:在权限表中加menu_id,(is_menu),pid
    数据库设计
                from django.db import models
    
                class Menu(models.Model):
                    """
                    菜单表
                    """
                    title = models.CharField(verbose_name='标题',max_length=32)
                    icon = models.CharField(verbose_name='图标',max_length=32)
    
    
                class Permission(models.Model):
                    """
                    权限表
                    """
                    url = models.CharField(verbose_name='URL(含正则)', max_length=128)
                    title = models.CharField(verbose_name='名称',max_length=32)
                    name = models.CharField(verbose_name='别名',max_length=32,unique=True)
    
                    menu = models.ForeignKey(verbose_name='管理菜单',to='Menu',to_field='id',null=True,blank=True)
                    parent = models.ForeignKey(verbose_name='父菜单',to='Permission',null=True,blank=True)
                    
                    
                class Role(models.Model):
                    """
                    角色表
                    """
                    title = models.CharField(verbose_name='名称', max_length=32)
                    permissions = models.ManyToManyField(verbose_name='关联权限',to='Permission')
                    
                    
                class UserInfo(models.Model):
                    """
                    用户表
                    """
                    username = models.CharField(verbose_name='用户名',max_length=32)
                    password = models.CharField(verbose_name='密码',max_length=64)
                    roles = models.ManyToManyField(verbose_name='关联角色',to='Role')
        
    数据库设计

    给url取别名,且别名不能重复
    创建表时类别首字母大写
    1对多时,将外键关联的字段写在多的表中;多对多时,写在常用查询的位置
    3.写model中代码,数据填充
    4.去掉web相关内容
    上一节功能去掉
                - 去掉web app
                - url.py
                    urlpatterns = [
                        url(r'^admin/', admin.site.urls),
                        # url(r'^web/', include('web.urls')),
                    ]
                - settings.py 
                    去掉 MENU_LIST
                    去掉注册的app:    'web.apps.WebConfig',
    View Code

    5.取数据(权限和菜单信息)
    可以连续跨表
    获取权限信息,获取可以做菜单的权限信息

    6.复制service文件夹,完成权限初始化
    复制中间件中权限 可以去写好的中间件中参考引入
    注册中间件 设置默认选中菜单

    权限控制

    7.动态生成二级菜单
    注意menu.html中去掉if is_menu判断

    模板:权限校验rbac+菜单menu+模板样式

    8.用户列表显示
    如果没有权限,按钮不展示
    ---粒度控制到按钮级别(要有别名name)
    自定义filter函数(因为在模板语言中不能用inclusion_tag)
    注意使用:{% if 'xxx'|permission:request %} {% endif %}
    操作、表格业没有也加if即可

    9.应用-使用权限系统
    通过别名反向生成url

    共9步
    1. 拷贝rbac应用
            
            2. 删除rbac/migrations目录中除 __init__.py 以外的所有文件
            
            3. 配置文件中注册 rbac的app
                INSTALLED_APPS = [
                    ...
                    'rbac.apps.RbacConfig',
                ]
            
            4. 数据库迁移 
                python manage.py makemigrations
                python manage.py migrate
                
            
            5. 编写测试系统的业务逻辑
                如果使用rbac中的模板的话,需要先删除layout.html中的:
                     <!-- 导入xxxxxxx模块 -->
                    {% load rbac %}
                    <!-- 执行get_menu函数并传递了一个参数 -->
                    {% get_menu request %}
            
                业务逻辑开发完毕....
        
            6. 设置权限相关的配置文件
                # ############################ 权限+菜单相关配置 #############################
                RBAC_PERMISSION_SESSION_KEY = "ijksdufwesdfs"
                RBAC_MENU_SESSION_KEY = "rtwsdfgwerffsd"
    
                VALID_LIST = [
                    '/api/login/',
                    '/admin/.*'
                ]
                
            7. 基于django admin 录入权限数据
                - 菜单 
                - 权限 
                - 权限角色关系表
                - 角色 
                - 用户角色关系表
                - 用户 
                
            8. 权限和菜单信息的应用
                - 用户登录:初始化权限和菜单信息
                    def login(request):
                        """
                        用户登录
                        :param request:
                        :return:
                        """
                        if request.method == "GET":
                            return render(request, 'api/login.html')
                        
                        user = request.POST.get('user')
                        pwd = request.POST.get('pwd')
                        
                        user = rbac_model.UserInfo.objects.filter(username=user, password=pwd).first()
                        if not user:
                            return render(request, 'api/login.html', {'msg': '用户名或密码错误'})
                        # ############ 看这里 ############
                        init_permission(user, request)
                        
                        return redirect('/api/app/list/')
                - 中间件:权限判断
                    settings.py 
                        MIDDLEWARE = [
                            ...
                            'rbac.middlewares.rbac.RBACMiddleware',
                        ]
                - inclusion_tag:动态生成菜单 
                    layout.html 
                        <div class="menu-body">
                            {% load rbac %}
    
                            {% get_menu request %}
                        </div>
                            
                
            9. 控制页面按钮
                
                {% extends 'layout.html' %}
                
                {% load rbac %} 
    
                {% block content %}
                    <h1>应用列表</h1>
                    
                    {% if 'app_add'|permission:request %}
                        <a class="btn btn-primary" href="{% url 'app_add' %}">添加</a>
                    {% endif %}
                    
                    <table class="table table-bordered">
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>姓名</th>
                                 {% if "app_edit"|permission:request or "app_del"|permission:request %}
                                <th>操作</th>
                                {% endif %}
                            </tr>
                        </thead>
                        <tbody>
                            {% for row in app_queryset %}
                                <tr>
                                    <td>{{ row.id }}</td>
                                    <td>{{ row.title }}</td>
                                    {% if "app_edit"|permission:request or "app_del"|permission:request %}
                                    <td>
                                        {% if "app_edit"|permission:request %}
                                            <a href="{% url 'app_edit' row.id %}">编辑</a>
                                        {% endif %}
                                        {% if "app_del"|permission:request %}
                                            <a href="{% url 'app_del' row.id %}/">删除</a>
                                        {% endif %}
                                    </td>
                                    {% endif %}
                                </tr>
                            {% endfor %}
                        </tbody>
                    </table>
    
    
                {% endblock %}
                
    步骤

    权限和菜单的应用,按钮的控制引用filter

    小结-------
    权限可以被应用到任何一个系统
    总结:
        1. 保存的代码:
            - 上一节示例:auto - 7 - 静态的菜单示例(最终版).zip 
            - 本节示例:nb_test.zip 
            
        2. 权限相关--记住
            
            1. 权限系统是如何实现的?
                基于角色的权限控制(rbac)
                
            2. 权限系统中用了哪些表?表中都有哪些字段?
            --见上面
        
            3. 你用中间件实现过什么?为什么使用中间件?
                rbac对权限的控制。
                所有的请求都会走中间件,所以权限控制在中间件中。
            4. 你认为哪里最难搞?
                - 动态二级菜单+默认选中
                - 构建菜单和权限的数据结构时。
                
            5. 其他
                ...
        流程总结:用户登录--将权限和菜单放入session中(定义一个init_permission),通过中间件进行权限的判断。通过inclusion_tag:动态生成菜单
    控制页面按钮用filter实现
    wsgi,中间件--描述清楚
    初始化--中间件--inlcusiontag 三块代码
    # ############################ 权限+菜单相关配置 #############################
    RBAC_PERMISSION_SESSION_KEY = "ijksdufwesdfs"
    RBAC_MENU_SESSION_KEY = "rtwsdfgwerffsd"
    
    VALID_LIST = [
        '/app01/login/',
        '/admin/.*'
    ]
    settings
    import re
    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    from django.shortcuts import HttpResponse
    
    
    class RBACMiddleware(MiddlewareMixin):
        """
        用户权限校验的中间件
        """
        def process_request(self, request):
            """
            权限校验
                1. 当前请求的URL
                2. 去Session中获取当前用户拥有的所有的权限
                3. 权限校验
            :param request:
            :return:
            """
            
            current_url = request.path_info
            # 1. 白名单处理
            for valid in settings.VALID_LIST:
                if re.match(valid,current_url):
                    return None
                
            # 2. 获取权限信息
            permission_dict = request.session.get(settings.RBAC_PERMISSION_SESSION_KEY)
            if not permission_dict:
                return HttpResponse('当前用户无权限信息,请重新登录!')
            """
            permission_dict = {
                'user_list': {'url': '/app01/user/', 'menu_id': 1, 'parent_name': None},
                'user_add': {'url': '/app01/user/add/', 'menu_id': None, 'parent_name': 'user_list'},
                'user_edit': {'url': '/app01/user/edit/(\d+)', 'menu_id': None, 'parent_name': 'user_list'},
                'order': {'url': '/app01/order/', 'menu_id': 2, 'parent_name': None}
            }
            """
            
            # 3. 权限匹配
            match = False
            for k,v in permission_dict.items():
                reg = "^%s$" % v['url']
                if re.match(reg,current_url):
                    # 用于以后生成菜单时候,设置默认选中的菜单。
                    if v['menu_id']:
                        request.default_selected_menu_name = k
                    else:
                        request.default_selected_menu_name = v['parent_name']
                    match = True
                    break
                    
            if not match:
                return HttpResponse('无权访问')
    1-Middleware中rbac.py
    from django.conf import settings
    
    
    def init_permission(user,request):
        """
        用户初始化,将权限信息和菜单信息放入session中。
        :param user: 当前登录的用户对象
        :param request:  请求相关的所有数据
        :return:
        """
        permission_menu_list = user.roles.filter(permissions__isnull=False).distinct().values(
            'permissions__title',
            'permissions__url',
            'permissions__name',
            'permissions__menu_id',  # 菜单相关
            'permissions__menu__title',
            'permissions__menu__icon',
            'permissions__parent_id',  # 父权限相关
            'permissions__parent__name'
        )
        
        # 2.3 获取当前用户拥有的所有权限信息 + 获取当前用户拥有的所有权限信息
        permission_dict = {}
        menu_dict = {}
        
        for item in permission_menu_list:
            # 添加权限字典中
            name = item['permissions__name']
            url = item['permissions__url']
            menu_id = item['permissions__menu_id']
            parent_name = item['permissions__parent__name']
            permission_dict[name] = {'url': url, 'menu_id': menu_id, 'parent_name': parent_name}
            
            # 添加到菜单字典中(只要可以成为菜单的权限)
            if menu_id:
                menu_id = item['permissions__menu_id']
                if menu_id in menu_dict:
                    menu_dict[menu_id]['children'].append(
                        {'title': item['permissions__title'], 'url': item['permissions__url'],
                         'name': item['permissions__name']})
                else:
                    menu_dict[menu_id] = {
                        'title': item['permissions__menu__title'],
                        'icon': item['permissions__menu__icon'],
                        'class': 'hide',
                        'children': [
                            {'title': item['permissions__title'], 'url': item['permissions__url'],
                             'name': item['permissions__name']}
                        ]
                    }
        
        
        
        request.session[settings.RBAC_PERMISSION_SESSION_KEY] = permission_dict
        request.session[settings.RBAC_MENU_SESSION_KEY] = menu_dict
    2-service中permission.py
    menu-html
    
    <div class="multi-menu">
        {% for item in menus %}
            <div class="item">
                <div class="title"><span class="icon-wrap">
                    <i class="fa {{ item.icon }}"></i></span> {{ item.title }}
                </div>
                <div class="body {{ item.class }}">
                    {% for child in item.children %}
                        <a class="{{ child.class }}" href="{{ child.url }}">{{ child.title }}</a>
                    {% endfor %}
                </div>
            </div>
        {% endfor %}
    </div>
    
    
    
    
    layout.html
    
    {% load staticfiles %}
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>路飞学城</title>
        <link rel="shortcut icon" href="{% static 'rbac/imgs/luffy-study-logo.png' %} ">
        <link rel="stylesheet" href="{% static 'rbac/plugins/bootstrap/css/bootstrap.css' %} "/>
        <link rel="stylesheet" href="{% static 'rbac/plugins/font-awesome/css/font-awesome.css' %} "/>
        <link rel="stylesheet" href="{% static 'rbac/css/commons.css' %} "/>
        <link rel="stylesheet" href="{% static 'rbac/css/nav.css' %} "/>
        <link rel="stylesheet" href="{% static 'rbac/css/menu.css' %} "/>
        <style>
            body {
                margin: 0;
            }
    
            .no-radius {
                border-radius: 0;
            }
    
            .no-margin {
                margin: 0;
            }
    
            .pg-body > .left-menu {
                background-color: #EAEDF1;
                position: absolute;
                left: 1px;
                top: 48px;
                bottom: 0;
                 220px;
                border: 1px solid #EAEDF1;
                overflow: auto;
            }
    
            .pg-body > .right-body {
                position: absolute;
                left: 225px;
                right: 0;
                top: 48px;
                bottom: 0;
                overflow: scroll;
                border: 1px solid #ddd;
                border-top: 0;
                font-size: 13px;
                min- 755px;
            }
    
            .navbar-right {
                float: right !important;
                margin-right: -15px;
            }
    
            .luffy-container {
                padding: 15px;
            }
    
    
        </style>
    </head>
    <body>
    
    <div class="pg-header">
        <div class="nav">
            <div class="logo-area left">
                <a href="#">
                    <img class="logo" src="{% static 'rbac/imgs/logo.svg' %}">
                    <span style="font-size: 18px;">路飞学城 </span>
                </a>
            </div>
    
            <div class="left-menu left">
                <a class="menu-item">资产管理</a>
                <a class="menu-item">用户信息</a>
                <a class="menu-item">路飞管理</a>
                <div class="menu-item">
                    <span>使用说明</span>
                    <i class="fa fa-caret-down" aria-hidden="true"></i>
                    <div class="more-info">
                        <a href="#" class="more-item">管他什么菜单</a>
                        <a href="#" class="more-item">实在是编不了</a>
                    </div>
                </div>
            </div>
    
            <div class="right-menu right clearfix">
    
                <div class="user-info right">
                    <a href="#" class="avatar">
                        <img class="img-circle" src="{% static 'rbac/imgs/default.png' %}">
                    </a>
    
                    <div class="more-info">
                        <a href="#" class="more-item">个人信息</a>
                        <a href="#" class="more-item">注销</a>
                    </div>
                </div>
    
                <a class="user-menu right">
                    消息
                    <i class="fa fa-commenting-o" aria-hidden="true"></i>
                    <span class="badge bg-success">2</span>
                </a>
    
                <a class="user-menu right">
                    通知
                    <i class="fa fa-envelope-o" aria-hidden="true"></i>
                    <span class="badge bg-success">2</span>
                </a>
    
                <a class="user-menu right">
                    任务
                    <i class="fa fa-bell-o" aria-hidden="true"></i>
                    <span class="badge bg-danger">4</span>
                </a>
            </div>
    
        </div>
    </div>
    <div class="pg-body">
        <div class="left-menu">
            <div class="menu-body">
                 <!-- 导入xxxxxxx模块 -->
                {% load rbac %}
                <!-- 执行get_menu函数并传递了一个参数 -->
                {% get_menu request %}
    
            </div>
        </div>
        <div class="right-body">
            <div>
                <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;">
    
                    <li><a href="#">首页</a></li>
                    <li class="active">客户管理</li>
    
                </ol>
            </div>
            {% block content %} {% endblock %}
        </div>
    </div>
    
    
    <script src="{% static 'rbac/js/jquery-3.3.1.min.js' %} "></script>
    <script src="{% static 'rbac/plugins/bootstrap/js/bootstrap.js' %} "></script>
    {% block js %} {% endblock %}
    <script>
        $(function () {
            $('.multi-menu .title').click(function () {
                $(this).next().toggleClass('hide');
            });
        })
    </script>
    </body>.
    </html>
    3-html
    from django.template import Library
    from django.conf import settings
    
    register = Library()
    
    @register.filter
    def permission(name,request):
        if name in request.session.get(settings.RBAC_PERMISSION_SESSION_KEY):
            return True
    
    
    
    @register.inclusion_tag('rbac/menu.html')
    def get_menu(request):
        """
        动态生成二级菜单
        :param request:
        :return:
        """
        menu_dict = request.session.get(settings.RBAC_MENU_SESSION_KEY)
        """
        {
            1: {
                'title': '用户管理',
                'icon': 'fa-clipboard',
                'class':'',
                'children': [
                    {'title': '用户列表', 'url': '/app01/user/', 'name': 'user_list','class':'active'}
                ]
            },
            2: {
                'title': '商品管理',
                'icon': 'fa-clipboard',
                'class':'hide',
                'children': [
                    {'title': '订单列表', 'url': '/app01/order/', 'name': 'order'},
                    {'title': '个人中心', 'url': '/app01/certer/', 'name': 'center'}
                ]
            }
        }
        """
        for k,v in menu_dict.items():
            for child in v['children']:
                name = child['name']
                if request.default_selected_menu_name == name:
                    child['class'] = 'active'
                    v['class'] = ''
                    
        return {'menus': list(menu_dict.values()) }
    4-templatetags中rbac
    
    
  • 相关阅读:
    Java中使用Jedis操作Redis
    Predicate与filter
    Joiner的用法
    Immutable集合
    【Excle数据透视】如何在数据透视表字段列表中显示更多的字段
    【Excle数据透视表】如何显示/隐藏数据透视表字段列表
    【Excle数据透视】如何创建多条件汇总的数据透视表
    【Excle数据透视表】如何创建非共享缓存的数据透视表
    【Excle数据透透视表】如何删除数据透视表
    【Excle数据透视表】如何复制数据透视表
  • 原文地址:https://www.cnblogs.com/lijie123/p/9839888.html
Copyright © 2020-2023  润新知