权限控制和菜单管理
权限控制到路径导航(面包屑)(models.py, )
权限控制到按钮(models.py, )
工作流程:
1. 登陆 中间件中通过白名单校验
2. 登陆成功 查询权限信息 保存在session中(json序列化)
3. 跳转至其他页面 进行权限的校验
1. 中间件中获取到session中的权限信息
2. 循环权限列表 用正则匹配当前的url地址
1. 匹配成功 return
2. 都没匹配成功 没有权限
from django.db import models # Create your models here. class Permission(models.Model): """ 权限表 """ url = models.CharField(max_length=256, verbose_name='权限', unique=True) title = models.CharField(max_length=32, verbose_name='标题') def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名称') permissions = models.ManyToManyField('Permission', blank=True) def __str__(self): return self.name class User(models.Model): """ 用户表 """ name = models.CharField(max_length=32, verbose_name='用户名') pwd = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField('Role', blank=True) def __str__(self): return self.name
from django.shortcuts import render, redirect, reverse from rbac import models from django.conf import settings def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') obj = models.User.objects.filter(name=user, pwd=pwd).first() if not obj: return render(request, 'login.html', {'error': '用户名或密码错误'}) # 保存权限信息 # 查询 permission_list = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__title', 'permissions__url').distinct() request.session[settings.PERMISSION_SESSION_KEY] = list(permission_list) # 跳转 return redirect(reverse('customer_list')) return render(request, 'login.html')
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import HttpResponse, redirect, reverse import re class RbacMidlleware(MiddlewareMixin): def process_request(self, request): # 获取当前访问的url url = request.path_info # 白名单 for i in settings.WHITE_LIST: if re.match(i, url): return # 获取权限信息 permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) print(permission_list) if not permission_list: return redirect(reverse('login')) # 权限的校验 for i in permission_list: if re.match("^{}$".format(i['permissions__url']), url): return return HttpResponse('没有访问权限')
from django.db import models # Create your models here. class Permission(models.Model): """ 权限表 is_menu True 表示当前的权限是一个菜单 False 表示当前的权限不是一个菜单,只是普通的权限 """ url = models.CharField(max_length=256, verbose_name='权限', unique=True) title = models.CharField(max_length=32, verbose_name='标题') is_menu = models.BooleanField(verbose_name='是否是菜单', default=False) icon = models.CharField(max_length=56, verbose_name='图标', blank=True, null=True) def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名称') permissions = models.ManyToManyField('Permission', blank=True) def __str__(self): return self.name class User(models.Model): """ 用户表 """ name = models.CharField(max_length=32, verbose_name='用户名') pwd = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField('Role', blank=True) def __str__(self): return self.name
from django.shortcuts import render, redirect, reverse from rbac import models from rbac.service.permission import init_permission def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') obj = models.User.objects.filter(name=user, pwd=pwd).first() if not obj: return render(request, 'login.html', {'error': '用户名或密码错误'}) # 登陆成功后 权限信息的初始化(保存权限信息和菜单信息) init_permission(request,obj) # 跳转 return redirect(reverse('customer_list')) return render(request, 'login.html')
from django.conf import settings def init_permission(request, obj): # 保存权限信息 # 查询 permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__title', 'permissions__url', 'permissions__is_menu', 'permissions__icon', ).distinct() # 存放权限信息的列表 permission_list = [] # 存放菜单信息的列表 menu_list = [] for item in permission_query: # 将权限信息放入permission_list permission_list.append({'url': item['permissions__url']}) # 将菜单的信息放入menu_list if item['permissions__is_menu']: menu_list.append({'url': item['permissions__url'], 'icon': item['permissions__icon'], 'title': item['permissions__title']}) # 权限信息放入session request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 菜单信息放入session request.session[settings.MENU_SESSION_KEY] = menu_list
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import HttpResponse, redirect, reverse import re class RbacMidlleware(MiddlewareMixin): def process_request(self, request): # 获取当前访问的url url = request.path_info # 白名单 for i in settings.WHITE_LIST: if re.match(i, url): return # 获取权限信息 permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) print(permission_list) # 登陆后没有权限 if not permission_list: return redirect(reverse('login')) # 权限的校验 for i in permission_list: if re.match("^{}$".format(i['url']), url): return return HttpResponse('没有访问权限')
<div class="static-menu"> {# <a href="/customer/list/" class="active">#} {# <span class="icon-wrap"><i class="fa fa-connectdevelop"></i></span> 客户管理</a>#} {# <a href="/payment/list/">#} {# <span class="icon-wrap"><i class="fa fa-code-fork"></i></span> 账单管理</a>#} {% for menu in menu_list %} <a href="{{ menu.url }}" class="{{ menu.class }}"> <span class="icon-wrap "><i class="fa {{ menu.icon }}"></i></span> {{ menu.title }}</a> {% endfor %} </div>
from django import template import re register = template.Library() from django.conf import settings # from luffy_permission import settings @register.inclusion_tag('menu.html') def menu(request): menu_list = request.session.get(settings.MENU_SESSION_KEY) url = request.path_info for item in menu_list: if re.match(item['url'], url): item['class'] = 'active' return {'menu_list': menu_list}
from django.db import models class Menu(models.Model): """ 菜单表 一级菜单 """ name = models.CharField(max_length=32) icon = models.CharField(max_length=56, verbose_name='图标', blank=True, null=True) def __str__(self): return self.name class Permission(models.Model): """ 权限表 是否管联一级菜单 权限 关联 菜单 表示 当前的权限是二级菜单 权限 不关联 菜单 表示 当前的权限不是菜单 """ url = models.CharField(max_length=256, verbose_name='权限', unique=True) title = models.CharField(max_length=32, verbose_name='标题') menu = models.ForeignKey('Menu', blank=True, null=True,) def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名称') permissions = models.ManyToManyField('Permission', blank=True) def __str__(self): return self.name class User(models.Model): """ 用户表 """ name = models.CharField(max_length=32, verbose_name='用户名') pwd = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField('Role', blank=True) def __str__(self): return self.name
from django.shortcuts import render, redirect, reverse
from rbac import models
from rbac.service.permission import init_permission
def login(request):
if request.method == 'POST':
user = request.POST.get('user')
pwd = request.POST.get('pwd')
obj = models.User.objects.filter(name=user, pwd=pwd).first()
if not obj:
return render(request, 'login.html', {'error': '用户名或密码错误'})
# 登陆成功后 权限信息的初始化(保存权限信息和菜单信息)
init_permission(request,obj)
# 跳转
return redirect(reverse('customer_list'))
return render(request, 'login.html')
from django.conf import settings def init_permission(request, obj): # 保存权限信息 # 查询 permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__title', 'permissions__url', 'permissions__menu__name', 'permissions__menu__icon', 'permissions__menu__id', ).distinct() print(permission_query) # 存放权限信息的列表 permission_list = [] # 存放菜单信息的列表 menu_dict = {} for item in permission_query: # 将权限信息放入permission_list permission_list.append({'url': item['permissions__url']}) # 放入菜单信息 menu_id = item.get('permissions__menu__id') if not menu_id: continue if menu_id not in menu_dict: menu_dict[menu_id] = {'name': item['permissions__menu__name'], 'icon': item['permissions__menu__icon'], 'children': [{'title': item['permissions__title'], 'url': item['permissions__url']}]} else: menu_dict[menu_id]['children'].append( {'title': item['permissions__title'], 'url': item['permissions__url']}) # 权限信息放入session request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 菜单信息放入session request.session[settings.MENU_SESSION_KEY] = menu_dict
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import HttpResponse, redirect, reverse import re class RbacMidlleware(MiddlewareMixin): def process_request(self, request): # 获取当前访问的url url = request.path_info # 白名单 for i in settings.WHITE_LIST: if re.match(i, url): return # 获取权限信息 permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) print(permission_list) # 登陆后没有权限 if not permission_list: return redirect(reverse('login')) # 权限的校验 for i in permission_list: if re.match("^{}$".format(i['url']), url): return return HttpResponse('没有访问权限')
<div class="multi-menu"> {% for menu in menu_list %} <div class="item"> <div class="title"><i class="fa {{ menu.icon }}"></i> {{ menu.name }}</div> <div class="body"> {% for child in menu.children %} <a href="{{ child.url }}"> {{ child.title }}</a> {% endfor %} </div> </div> {% endfor %} </div>
from django import template import re register = template.Library() from django.conf import settings # from luffy_permission import settings @register.inclusion_tag('menu.html') def menu(request): menu_dict = request.session.get(settings.MENU_SESSION_KEY) return {'menu_list': menu_dict.values()}
from django.db import models class Menu(models.Model): """ 菜单表 一级菜单 """ name = models.CharField(max_length=32) icon = models.CharField(max_length=56, verbose_name='图标', blank=True, null=True) weight = models.IntegerField(default=1) def __str__(self): return self.name class Permission(models.Model): """ 权限表 menu_id 有 表示当前的权限是二级菜单 也是父权限 没有 就是一个普通的权限 parent_id 有 表示当前的权限是一个子权限 没有 父权限 """ url = models.CharField(max_length=256, verbose_name='权限', unique=True) title = models.CharField(max_length=32, verbose_name='标题') menu = models.ForeignKey('Menu', blank=True, null=True, ) parent = models.ForeignKey('Permission',blank=True, null=True,) def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名称') permissions = models.ManyToManyField('Permission', blank=True) def __str__(self): return self.name class User(models.Model): """ 用户表 """ name = models.CharField(max_length=32, verbose_name='用户名') pwd = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField('Role', blank=True) def __str__(self): return self.name
from django.shortcuts import render, redirect, reverse from rbac import models from rbac.service.permission import init_permission def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') obj = models.User.objects.filter(name=user, pwd=pwd).first() if not obj: return render(request, 'login.html', {'error': '用户名或密码错误'}) # 登陆成功后 权限信息的初始化(保存权限信息和菜单信息) init_permission(request,obj) # 跳转 return redirect(reverse('customer_list')) return render(request, 'login.html')
from django.conf import settings def init_permission(request, obj): # 保存权限信息 # 查询 permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__title', 'permissions__url', 'permissions__menu__name', 'permissions__menu__icon', 'permissions__menu__weight', 'permissions__menu__id', 'permissions__id', 'permissions__parent_id', ).distinct() print(permission_query) # 存放权限信息的列表 permission_dict = {} # 存放菜单信息的列表 menu_dict = {} for item in permission_query: # 将权限信息放入permission_list permission_dict[item['permissions__id']] = {'url': item['permissions__url'], 'pid': item['permissions__parent_id'], 'id': item['permissions__id'], 'title': item['permissions__title'], } # 放入菜单信息 menu_id = item.get('permissions__menu__id') # 不是菜单 if not menu_id: continue if menu_id not in menu_dict: menu_dict[menu_id] = {'name': item['permissions__menu__name'], 'icon': item['permissions__menu__icon'], 'weight': item['permissions__menu__weight'], 'children': [{'id': item['permissions__id'], 'title': item['permissions__title'], 'url': item['permissions__url']}]} else: menu_dict[menu_id]['children'].append( {'id': item['permissions__id'], 'title': item['permissions__title'], 'url': item['permissions__url']}) # 权限信息放入session request.session[settings.PERMISSION_SESSION_KEY] = permission_dict # 菜单信息放入session request.session[settings.MENU_SESSION_KEY] = menu_dict
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import HttpResponse, redirect, reverse import re class RbacMidlleware(MiddlewareMixin): def process_request(self, request): # 获取当前访问的url url = request.path_info # 白名单 for i in settings.WHITE_LIST: if re.match(i, url): return # 获取权限信息 permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY) print(permission_dict) # 登陆后没有权限 if not permission_dict: return redirect(reverse('login')) request.current_menu_id = None request.breadcrumb_list = [ {'title': '首页', 'url': '/index/'} ] # 需要登录但是不需要权限校验 for i in settings.NO_PERMISSION_LIST: if re.match(i, url): return # 权限的校验 for item in permission_dict.values(): # {url id pid } if re.match("^{}$".format(item['url']), url): # 要显示二级菜单的id 权限的id pid = item.get('pid') id = item.get('id') if pid: # 当前访问的权限 是子权限 找父权限进行显示 request.current_menu_id = pid request.breadcrumb_list.append( {'title': permission_dict[str(pid)]['title'], 'url': permission_dict[str(pid)]['url']}) request.breadcrumb_list.append({'title': item['title'], 'url': item['url']}) else: # 当前访问的权限 是父权限 二级菜单 找自己进行显示 request.current_menu_id = id request.breadcrumb_list.append({'title': item['title'], 'url': item['url']}) return return HttpResponse('没有访问权限')
<div> <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> {% for breadcrumb in breadcrumb_list %} {% if forloop.last %} <li class="active">{{ breadcrumb.title }}</li> {% else %} <li><a href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a></li> {% endif %} {% endfor %} </ol> </div>
from django import template import re from collections import OrderedDict register = template.Library() from django.conf import settings # from luffy_permission import settings @register.inclusion_tag('menu.html') def menu(request): menu_dict = request.session.get(settings.MENU_SESSION_KEY) order_dic = OrderedDict() for key in sorted(menu_dict, key=lambda i: menu_dict[i]['weight'], reverse=True): order_dic[key] = item = menu_dict[key] item['class'] = 'hide' for i in item['children']: # if re.match("^{}$".format(i['url']), request.path_info): if i['id'] == request.current_menu_id: i['class'] = 'active' item['class'] = '' break return {'menu_list': order_dic.values()} @register.inclusion_tag('breadcrumb.html') def breadcrumb(request): return {'breadcrumb_list': request.breadcrumb_list}
from django.db import models class Menu(models.Model): """ 菜单表 一级菜单 """ name = models.CharField(max_length=32) icon = models.CharField(max_length=56, verbose_name='图标', blank=True, null=True) weight = models.IntegerField(default=1) def __str__(self): return self.name class Permission(models.Model): """ 权限表 menu_id 有 表示当前的权限是二级菜单 也是父权限 没有 就是一个普通的权限 parent_id 有 表示当前的权限是一个子权限 没有 父权限 """ url = models.CharField(max_length=256, verbose_name='权限', unique=True) title = models.CharField(max_length=32, verbose_name='标题') name = models.CharField(max_length=32, verbose_name='URL别名',unique=True) menu = models.ForeignKey('Menu', blank=True, null=True, ) parent = models.ForeignKey('Permission', blank=True, null=True, ) def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='角色名称') permissions = models.ManyToManyField('Permission', blank=True) def __str__(self): return self.name class User(models.Model): """ 用户表 """ name = models.CharField(max_length=32, verbose_name='用户名') pwd = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField('Role', blank=True) def __str__(self): return self.name
from django.shortcuts import render, redirect, reverse from rbac import models from rbac.service.permission import init_permission def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') obj = models.User.objects.filter(name=user, pwd=pwd).first() if not obj: return render(request, 'login.html', {'error': '用户名或密码错误'}) # 登陆成功后 权限信息的初始化(保存权限信息和菜单信息) init_permission(request,obj) # 跳转 return redirect(reverse('customer_list')) return render(request, 'login.html')
from django.conf import settings def init_permission(request, obj): # 保存权限信息 # 查询 permission_query = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__title', 'permissions__url', 'permissions__menu__name', 'permissions__menu__icon', 'permissions__menu__weight', 'permissions__menu__id', 'permissions__id', 'permissions__parent_id', 'permissions__name', 'permissions__parent__name', ).distinct() print(permission_query) # 存放权限信息的列表 permission_dict = {} # 存放菜单信息的列表 menu_dict = {} for item in permission_query: # 将权限信息放入permission_list permission_dict[item['permissions__name']] = {'url': item['permissions__url'], 'pid': item['permissions__parent_id'], 'pname': item['permissions__parent__name'], 'id': item['permissions__id'], 'title': item['permissions__title'], } # 放入菜单信息 menu_id = item.get('permissions__menu__id') # 不是菜单 if not menu_id: continue if menu_id not in menu_dict: menu_dict[menu_id] = {'name': item['permissions__menu__name'], 'icon': item['permissions__menu__icon'], 'weight': item['permissions__menu__weight'], 'children': [{'id': item['permissions__id'], 'title': item['permissions__title'], 'url': item['permissions__url']}]} else: menu_dict[menu_id]['children'].append( {'id': item['permissions__id'], 'title': item['permissions__title'], 'url': item['permissions__url']}) # 权限信息放入session request.session[settings.PERMISSION_SESSION_KEY] = permission_dict # 菜单信息放入session request.session[settings.MENU_SESSION_KEY] = menu_dict
from django.utils.deprecation import MiddlewareMixin from django.conf import settings from django.shortcuts import HttpResponse, redirect, reverse import re class RbacMidlleware(MiddlewareMixin): def process_request(self, request): # 获取当前访问的url url = request.path_info # 白名单 for i in settings.WHITE_LIST: if re.match(i, url): return # 获取权限信息 permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY) print(permission_dict) # 登陆后没有权限 if not permission_dict: return redirect(reverse('login')) request.current_menu_id = None request.breadcrumb_list = [ {'title': '首页', 'url': '/index/'} ] # 需要登录但是不需要权限校验 for i in settings.NO_PERMISSION_LIST: if re.match(i, url): return # 权限的校验 for item in permission_dict.values(): # {url id pid } if re.match("^{}$".format(item['url']), url): # 要显示二级菜单的id 权限的id pid = item.get('pid') id = item.get('id') pname = item.get('pname') if pid: # 当前访问的权限 是子权限 找父权限进行显示 request.current_menu_id = pid request.breadcrumb_list.append( {'title': permission_dict[pname]['title'], 'url': permission_dict[pname]['url']}) request.breadcrumb_list.append({'title': item['title'], 'url': item['url']}) else: # 当前访问的权限 是父权限 二级菜单 找自己进行显示 request.current_menu_id = id request.breadcrumb_list.append({'title': item['title'], 'url': item['url']}) return return HttpResponse('没有访问权限')
<div> <ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> {% for breadcrumb in breadcrumb_list %} {% if forloop.last %} <li class="active">{{ breadcrumb.title }}</li> {% else %} <li><a href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a></li> {% endif %} {% endfor %} </ol> </div>
from django import template import re from collections import OrderedDict register = template.Library() from django.conf import settings # from luffy_permission import settings @register.inclusion_tag('menu.html') def menu(request): menu_dict = request.session.get(settings.MENU_SESSION_KEY) order_dic = OrderedDict() for key in sorted(menu_dict, key=lambda i: menu_dict[i]['weight'], reverse=True): order_dic[key] = item = menu_dict[key] item['class'] = 'hide' for i in item['children']: # if re.match("^{}$".format(i['url']), request.path_info): if i['id'] == request.current_menu_id: i['class'] = 'active' item['class'] = '' break return {'menu_list': order_dic.values()}
#rbac组件的应用 1. 拷贝rbac组件到新的项目中,并且要注册 2. 数据库的迁移 1. 先删除rbac下migrations下的除了init之外的文件 2. 修改用户表 1. class User(models.Model): """ 用户表 """ # name = models.CharField(max_length=32, verbose_name='用户名') # pwd = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField(Role, blank=True) # 关联用类 class Meta: abstract = True # 执行数据库迁移命令时不会生成具体的表,这张表做基类 2. 在新项目中用户表要去继承User 3. 执行数据库迁移的命令 3. 在根的urlconf中添加rbac的路由 url(r'rbac/', include('rbac.urls', namespace='rbac')) 4. 角色管理 添加角色 5. 菜单管理 给权重 6. 权限管理 1. 录入权限信息 2. 分配好菜单和父权限 7. 分配权限 1. 给角色分配权限 2. 给用户分配角色 8. 加上权限的控制 1. 加中间件 2. 权限的配置放在settings中 # 权限存放在session中的KEY PERMISSION_SESSION_KEY = 'permission' # 菜单存放在session中的KEY MENU_SESSION_KEY = 'menu' WHITE_LIST = [ r'^/login/$', r'^/reg/$', r'^/admin/.*', ] NO_PERMISSION_LIST = [ r'^/index/$', ] 3. 修改登录函数s 校验成功后权限信息的初始化 from rbac.service.permission import init_permission # 登录成功后 init_permission(request,obj) 9. 应用上二级菜单 在母板中使用 menu inclusion_tag {% load rbac %} {% menu request %} 引入css、js的效果 10. 路径导航 {% breadcrumb request %} 11. 权限控制到按钮 {% load rbac %} {% if request|has_permission:'add_customer' %} <a class="btn btn-sm btn-primary" style="margin-bottom: 5px" href="{% reverse_url request 'add_customer' %}">添加</a> {% endif %}