python 权限系统设计
权限系统表结构的设计
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('无权访问.')