Django_用户权限管理rbac


组成部分

1、初始化权限:login视图initial_permission,把权限信息放入session。initial_permission函数生成权限列表、菜单列表

2、中间件验证权限:在第一次登陆后,使用中间件的process_request检验用户的权限情况,同时,也有白名单RBAC_NO_AUTH_URL放在settings.py

3、simple_tag生成菜单

重点代码


#!/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


具体代码:

fork wupeiqi的,结合arya:https://github.com/fat39/pro_admin

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

posted @ 2018-11-20 16:10  fat39  阅读(212)  评论(0编辑  收藏  举报