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
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')
#!/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)
#!/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文件不存在')
# ############################## 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权限相关配置结束 ##############################
具体代码:
fork wupeiqi的,结合arya:https://github.com/fat39/pro_admin
fork wupeiqi的,有curd的actions models:https://github.com/fat39/Rbacdemo