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
相關文章
相關標籤/搜索