權限系統表結構的設計
from django.db import models # Create your models here. class UserInfo(models.Model): ''' 用戶表 ''' username=models.CharField(max_length=32,verbose_name='用戶名') password=models.CharField(max_length=64,verbose_name='密碼') roles=models.ManyToManyField(to='Role',verbose_name='擁有角色',) class Role(models.Model): ''' 角色表 ''' title=models.CharField(max_length=32,verbose_name='角色名稱') permissions=models.ManyToManyField(to='Permission',verbose_name='擁有權限') def __str__(self): return self.title class Permission(models.Model): ''' 權限表 ''' title=models.CharField(max_length=32,verbose_name='權限名稱') url=models.CharField(max_length=255,verbose_name='含權限的URL') code=models.CharField(max_length=32,verbose_name='權限代碼') group=models.ForeignKey(to='PermissionGroup',verbose_name='所屬權限組',on_delete=True) group_menu=models.ForeignKey(verbose_name='組內菜單',to='self',null=True,blank=True,related_name='gxx',on_delete=True) class PermissionGroup(models.Model): ''' 權限組 ''' caption=models.CharField(max_length=32,verbose_name='權限組名稱') menu=models.ForeignKey(to='Menu',on_delete=True) class Menu(models.Model): ''' 菜單表 ''' title=models.CharField(max_length=32)
權限系統需要在django項目settings.py 中設置如下
PERMISSION_DICT_SESSION_KEY='user_permission_dict_key' #權限字典存入session中 PERMISSION_MENU_SESSION_KEY='user_permission_menu_key' #權限目錄放入session中 REX_FORMAT = "^%s$" #url的匹配頭部和結尾設置 VALID_LIST = [ #url的訪問白名單 '/crm/login/', '^/admin/.*', ]
權限系統的app中建立services包,init_permission.py 文件 把權限的url存入session中,把權限的目錄存入session中
from django.conf import settings def init_permission(user,request): ''' 登錄用戶的權限初始化 :param user:登錄用戶對象 :param request: :return: ''' permisssions_list = user.roles.filter(permissions__id__isnull=False).values( 'permissions__id',#權限id 'permissions__title',#權限名稱 'permissions__url',#權限url 'permissions__code',#權限code 'permissions__group__id',#權限組id 'permissions__group_menu__id',#是否為組內菜單 'permissions__group__menu__id',#一級菜單id 'permissions__group__menu__title',#一級菜單名稱 ).distinct() #獲取權限信息+菜單放入session 中,以后生成動態菜單 permission_menu_list=[] for item in permisssions_list: val={ 'id':item['permissions__id'], 'title':item['permissions__title'], 'url':item['permissions__url'], 'pid':item['permissions__group_menu__id'], 'menu_id':item['permissions__group__menu__id'], 'menu_name':item['permissions__group__menu__title'], } permission_menu_list.append(val) request.session[settings.PERMISSION_MENU_SESSION_KEY]=permission_menu_list #獲取權限信息放入session中 permission_dict = {} for permission in permisssions_list: group_ip = permission['permissions__group__id'] url = permission['permissions__url'] code = permission['permissions__code'] if group_ip in permission_dict: permission_dict[group_ip]['urls'].append(url) permission_dict[group_ip]['codes'].append(code) else: permission_dict[group_ip] = {'urls': [url, ], 'codes': [code, ]} request.session[settings.PERMISSION_DICT_SESSION_KEY]=permission_dict
權限系統的app中建立templatetags包,rbac.py 文件 構造權限目錄
from django.template import Library from django.conf import settings import re register=Library() @register.inclusion_tag('rbac/menu.html') def menu(request): permission_menu_list = request.session[settings.PERMISSION_MENU_SESSION_KEY] current_url = request.path_info current_pxe = settings.REX_FORMAT # 構造二級菜單字典 per_dict = {} for per_item in permission_menu_list: if not per_item['pid']: per_dict[per_item['id']] = per_item for item in permission_menu_list: if not re.match(current_pxe % (item['url']), current_url): continue if item['pid']: per_dict[item['pid']]['active'] = True else: item['active'] = True # 生成菜單字典 menu_dict = {} for menu_item in per_dict.values(): if menu_item['menu_id'] in menu_dict: menu_dict[menu_item['menu_id']]['children'].append( {'id': menu_item['id'], "title": menu_item['title'], 'url': menu_item['url'], 'active': menu_item.get('active', False)}) # menu_item['menu_id']['children'].append({'id':menu_item['id'],"title":menu_item['title'],'url':menu_item['url'],'active':menu_item.get('active',False)}) if menu_item.get('active', False): menu_item['menu_id']['active']: True # menu_item['menu_id']['children'].append({'id':menu_item['id'],"title":menu_item['title'],'url':menu_item['url'],'active':menu_item.get('active',False)}) else: menu_dict[menu_item['menu_id']] = { 'menu_name': menu_item['menu_name'], 'active': menu_item.get('active', False), 'children': [{'id': menu_item['id'], "title": menu_item['title'], 'url': menu_item['url'], 'active': menu_item.get('active', False)}] } return {'menu_dict':menu_dict}
權限系統的app中建立templates/rbac/menu.html 文件 構造權限的html頁面
{% for menu in menu_dict.values %} <div class="con-menu"> <div class="menu-name">{{ menu.menu_name }}</div> <div class="menu-item"> {% for item in menu.children %} {% if item.active %} <a href="{{ item.url }}" class="active">{{ item.title }}</a> {% else %} <a href="{{ item.url }}">{{ item.title }}</a> {% endif %} {% endfor %} </div> </div> {% endfor %}
在 要使用權限系統的app中 的布局頁面,中把 menu.html 頁面個包含到目錄位置
{% load rbac %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> body{ margin: 0; } .pg-head{ height: 48px; background-color: darkgray; } .menu{ position: absolute; top:48px; left: 0px; bottom: 0px; width: 100px; background-color: whitesmoke; } .content{ position: absolute; top: 48px; left: 100px; right: 0px; bottom: 0px; overflow: auto; background-color: aliceblue; } .con-menu .menu-name{ background-color: beige; } .con-menu .menu-item a{ display: block; margin-left: 10px; } .con-menu .menu-item a.active{ color: aquamarine; } </style> </head> <body> <div class="pg-head">頭部菜單</div> <div class="pg-body"> <div class="menu"> {% menu request %} </div> <div class="content"> {% block content %} {% endblock %} </div> </div> </body> </html>
權限系統中的權限的驗證時放在中間件找那個實現的
MIDDLEWARE = [ #'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', #'django.middleware.cache.FetchFromCacheMiddleware', 'rbac.middlewares.rbac.RbacMiddleware', # 權限驗證的中間件 ]
權限中間件 在 rbac app 中創建 middlewares包創建rbac.py文件 在其中創建 繼承中間件的類,實現用戶登錄權限的驗證。
from django.conf import settings from django.shortcuts import HttpResponse import re class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super().__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response class RbacMiddleware(MiddlewareMixin): def process_request(self,request): #訪問白名單 current_url = request.path_info white_list=settings.VALID_LIST # if current_url=='/crm/login/': # return None for valid_item in white_list: if re.match(valid_item,current_url): return None #獲取用戶的權限信息 permission_dict=request.session.get(settings.PERMISSION_DICT_SESSION_KEY) #print(permission_dict) if not permission_dict: return HttpResponse('當前用戶無權限信息.') #匹配用戶是否擁有訪問當前頁面的權限 flag=False for permission_item in permission_dict.values(): urls=permission_item['urls'] codes=permission_item['codes'] for itme_url in urls: reg=settings.REX_FORMAT %(itme_url) if re.match(reg,current_url): request.permission_codes = codes print(request.permission_codes) flag=True break if flag: break if not flag: return HttpResponse('無權訪問.')