一、初始化權限: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
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權限相關配置結束 ##############################
具體代碼:git
fork wupeiqi的,結合arya:https://github.com/fat39/pro_admingithub
fork wupeiqi的,有curd的actions models:https://github.com/fat39/Rbacdemodjango