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;
            width: 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-width: 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

 


posted @ 2018-10-23 22:30  yuyou123  阅读(125)  评论(0编辑  收藏  举报