權限控制到路徑導航(面包屑)(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 %}