Django_用戶權限管理rbac


組成部分

一、初始化權限:login視圖initial_permission,把權限信息放入session。initial_permission函數生成權限列表、菜單列表css

二、中間件驗證權限:在第一次登錄後,使用中間件的process_request檢驗用戶的權限狀況,同時,也有白名單RBAC_NO_AUTH_URL放在settings.pyhtml

三、simple_tag生成菜單python

重點代碼


#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django.conf import settings
from .. import models


def initial_permission(request, user):
    """
    初始化權限,獲取當前用戶權限並添加到session中

    :param request: 請求對象
    :param user: 當前用戶對象
    :return:
    """
    # 1.獲取當前用戶全部角色 user.roles.all()
    # roles = user.roles.all()

    # 2.獲取角色對應的全部權限
    permission_list = user.roles.values('permissions__id', 'permissions__caption', 'permissions__url',
                                        'permissions__menu_id').distinct()

    permission_url_list = []
    permission_menu_list = []
    for item in permission_list:
        permission_url_list.append(item['permissions__url'])
        if item['permissions__menu_id']:
            permission_menu_list.append(item)

    # 3. 權限寫入session
    request.session[settings.RBAC_PERMISSION_URL_SESSION_KEY] = permission_url_list

    # 4. 菜單寫入session
    menu_list = list(models.Menu.objects.values('id', 'caption', 'parent_id'))
    request.session[settings.RBAC_MENU_PERMISSION_SESSION_KEY] = {
        settings.RBAC_MENU_KEY: menu_list,
        settings.RBAC_MENU_PERMISSION_KEY: permission_menu_list
    }


pro_admin(項目名)/arya/service/rbac.py
pro_admin(項目名)/arya/service/rbac.py
def login(self, request):
    """
    用戶登陸
    :param request:
    :return:
    """
    from arya import models
    from arya.service import rbac

    # 測試
    # obj = models.User.objects.get(id=1)
    # rbac.initial_permission(request, obj) # 初始化權限信息
    #
    # return HttpResponse('Login')

    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        from arya import models
        from arya.service import rbac

        user = request.POST.get('username')
        pwd = request.POST.get('password')
        obj = models.User.objects.filter(username=user, password=pwd).first()
        if obj:
            rbac.initial_permission(request, obj)
            return redirect('/arya/')
        else:
            return render(request, 'login.html')
視圖函數login


#!/usr/bin/env python
# -*- coding:utf-8 -*-

import re
from django.conf import settings
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
from django.utils.safestring import mark_safe


class RbacMiddleware(MiddlewareMixin):
    def process_request(self, request, *args, **kwargs):
        """
        檢查用戶是否具備權限訪問當前URL
        :param request:
        :param args:
        :param kwargs:
        :return:
        """

        """跳過無需權限訪問的URL"""
        for pattern in settings.RBAC_NO_AUTH_URL:
            if re.match(pattern, request.path_info):
                return None

        """獲取當前用戶session中的權限信息"""
        permission_url_list = request.session.get(settings.RBAC_PERMISSION_URL_SESSION_KEY)
        if not permission_url_list:
            return HttpResponse(settings.RBAC_PERMISSION_MSG)

        """當前URL和session中的權限進行匹配"""
        flag = False
        for url in permission_url_list:
            pattern = settings.RBAC_MATCH_PARTTERN.format(url)
            if re.match(pattern, request.path_info):
                flag = True
                break

        if not flag:
            if settings.DEBUG:
                return HttpResponse("無權訪問,你的權限有:<br/>" + mark_safe("<br/>".join(permission_url_list)))
            else:
                return HttpResponse(settings.RBAC_PERMISSION_MSG)

pro_admin/arya/middleware/rbac.py中間件處理權限


#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
import os
from django import template
from django.utils.safestring import mark_safe
from django.conf import settings

register = template.Library()


def process_menu_tree_data(request):
    """
    根據Session中獲取的菜單以及權限信息,結構化數據,生成特殊數據結構,如:
    [
        {id:1,caption:'菜單標題',parent_id:None,status:False,opened:False,child:[...]},
    ]
    PS: 最後一層的權限會有url,即:菜單跳轉的地址

    :param request:
    :return:
    """
    menu_permission_dict = request.session.get(settings.RBAC_MENU_PERMISSION_SESSION_KEY)
    if not menu_permission_dict:
        raise Exception('Session中未保存當前用戶菜單以及權限信息,請登陸後初始化權限信息!')

    """ session中獲取菜單和權限信息 """
    all_menu_list = menu_permission_dict[settings.RBAC_MENU_KEY]
    menu_permission_list = menu_permission_dict[settings.RBAC_MENU_PERMISSION_KEY]

    all_menu_dict = {}
    for row in all_menu_list:
        row['opened'] = False
        row['status'] = False
        row['child'] = []
        all_menu_dict[row['id']] = row

    """ 將權限信息掛靠在菜單上,並設置是否默認打開,以及默認顯示 """
    for per in menu_permission_list:
        item = {'id': per['permissions__id'], 'caption': per['permissions__caption'], 'url': per['permissions__url'],
                'parent_id': per['permissions__menu_id'],
                'opened': False,
                'status': True}
        menu_id = item['parent_id']
        all_menu_dict[menu_id]['child'].append(item)

        # 將當前URL和權限正則進行匹配,用於指示是否默認打開菜單
        pattern = settings.RBAC_MATCH_PARTTERN.format(item['url'])
        if re.match(pattern, request.path_info):
            item['opened'] = True

        if item['opened']:
            pid = menu_id
            while not all_menu_dict[pid]['opened']:
                all_menu_dict[pid]['opened'] = True
                pid = all_menu_dict[pid]['parent_id']
                if not pid:
                    break

        if item['status']:
            pid = menu_id
            while not all_menu_dict[pid]['status']:
                all_menu_dict[pid]['status'] = True
                pid = all_menu_dict[pid]['parent_id']
                if not pid:
                    break

    result = []
    for row in all_menu_list:
        pid = row['parent_id']
        if pid:
            all_menu_dict[pid]['child'].append(row)
        else:
            result.append(row)

    return result


def build_menu_tree_html(menu_list):
    tpl1 = """
        <div class='rbac-menu-item'>
            <div class='rbac-menu-header'>{0}</div>
            <div class='rbac-menu-body {2}'>{1}</div>
        </div>
    """
    tpl2 = """
        <a href='{0}' class='{1}'>{2}</a>
    """
    menu_str = ""
    for menu in menu_list:
        if not menu['status']:
            continue

        if menu.get('url'):
            menu_str += tpl2.format(menu['url'], 'rbac-active' if menu['opened'] else "" , menu['caption'])
        else:
            if menu.get('child'):
                child = build_menu_tree_html(menu.get('child'))
            else:
                child = ""
            menu_str += tpl1.format(menu['caption'], child, "" if menu['opened'] else 'rbac-hide')
    return menu_str


@register.simple_tag
def rbac_menu(request):
    """
    根據Session中當前用戶的菜單信息以及當前URL生成菜單
    :param request: 請求對象
    :return:
    """
    menu_tree_list = process_menu_tree_data(request)
    return mark_safe(build_menu_tree_html(menu_tree_list))


@register.simple_tag
def rbac_css():
    file_path = os.path.join('arya', 'theme', settings.RBAC_THEME, 'rbac.css')
    if os.path.exists(file_path):
        return mark_safe(open(file_path, 'r', encoding='utf-8').read())
    else:
        raise Exception('rbac主題CSS文件不存在')


@register.simple_tag
def rbac_js():
    file_path = os.path.join('arya', 'theme', settings.RBAC_THEME, 'rbac.js')
    if os.path.exists(file_path):
        return mark_safe(open(file_path, 'r', encoding='utf-8').read())
    else:
        raise Exception('rbac主題JavaScript文件不存在')

pro_admin/arya/templatetags/arya.py使用simple_tag生成菜單欄


# ############################## RBAC權限相關配置開始 ##############################
# session中保存權限信息的Key
RBAC_PERMISSION_URL_SESSION_KEY = "rbac_permission_url_session_key"

# Session中保存菜單和權限信息的Key
RBAC_MENU_PERMISSION_SESSION_KEY = "rbac_menu_permission_session_key"
RBAC_MENU_KEY = "rbac_menu_key"
RBAC_MENU_PERMISSION_KEY = "rbac_menu_permission_key"

# 匹配URL時指定規則
RBAC_MATCH_PARTTERN = "^{0}$"

# 無需權限控制的URL
RBAC_NO_AUTH_URL = [
    '/arya/login',
]

# 無權訪問時,頁面提示信息
RBAC_PERMISSION_MSG = "無權限訪問"

# 菜單主題
RBAC_THEME = "default"
# ############################## RBAC權限相關配置結束 ##############################
settings.py


具體代碼:git

fork wupeiqi的,結合arya:https://github.com/fat39/pro_admingithub

fork wupeiqi的,有curd的actions models:https://github.com/fat39/Rbacdemodjango

相關文章
相關標籤/搜索