用户角色权限 案例
#權限管理:rbac:role basic access control
# 一 根據用戶獲取權限, session中 中間件實現權限控制
# 二 菜單管理
#默認展開
#只顯示當前用戶菜單
# 一 根據用戶獲取權限, session中 中間件實現權限控制
# 二 菜單管理
#默認展開
#只顯示當前用戶菜單
一、權限用戶表
a:SQL表結構
from django.db import models# Create your models here. class User(models.Model):"""用戶表"""username = models.CharField(verbose_name='用戶名', max_length=32)password = models.CharField(verbose_name='密碼', max_length=64)email = models.EmailField(verbose_name='郵箱')def __str__(self):return self.usernameclass Role(models.Model):"""角色表"""caption = models.CharField(verbose_name='角色', max_length=32)def __str__(self):return self.captionclass User2Role(models.Model):"""用戶角色關系表"""user = models.ForeignKey(User, verbose_name='用戶', related_name='roles')role = models.ForeignKey(Role, verbose_name='角色', related_name='users')def __str__(self):return '%s-%s' % (self.user.username, self.role.caption,)class Menu(models.Model):"""菜單表"""caption = models.CharField(verbose_name='菜單名稱', max_length=32)parent = models.ForeignKey('self', verbose_name='父菜單', related_name='p', null=True, blank=True)def __str__(self):prev = ""parent = self.parentwhile True:if parent:prev = prev + '-' + str(parent.caption)parent = parent.parentelse:breakreturn '%s-%s' % (prev, self.caption,)class Permission(models.Model):"""權限"""caption = models.CharField(verbose_name='權限', max_length=32)url = models.CharField(verbose_name='URL正則', max_length=128)menu = models.ForeignKey(Menu, verbose_name='所屬菜單', related_name='permissions',null=True,blank=True)def __str__(self):return "%s-%s" % (self.caption, self.url,)class Action(models.Model):"""操作:增刪改查"""caption = models.CharField(verbose_name='操作標題', max_length=32)code = models.CharField(verbose_name='方法', max_length=32)def __str__(self):return self.captionclass Permission2Action2Role(models.Model):"""權限操作關系表"""permission = models.ForeignKey(Permission, verbose_name='權限URL', related_name='actions')action = models.ForeignKey(Action, verbose_name='操作', related_name='permissions')role = models.ForeignKey(Role, verbose_name='角色', related_name='p2as')class Meta:unique_together = (('permission', 'action', 'role'),)def __str__(self):return "%s-%s-%s" % (self.permission, self.action, self.role,) models.pyb.輸出菜單
urlpatterns = [url(r'^auth-menu.html$',view2.menu),] url.py from django.contrib import admin# Register your models here. from app02 import modelsadmin.site.register(models.User) admin.site.register(models.Role) admin.site.register(models.User2Role) admin.site.register(models.Menu) admin.site.register(models.Permission) admin.site.register(models.Action) admin.site.register(models.Permission2Action2Role) app02/admin.py def menu(request):"""需要用戶名或用戶ID,產出:用戶關聯所有菜單:param request::return:"""# 所有菜單:處理成當前用關聯的菜單all_menu_list = models.Menu.objects.all().values('id', 'caption', 'parent_id')"""[{'id':1, 'caption':'菜單1', parent_id:None},{'id':2, 'caption':'菜單2', parent_id:None},{'id':3, 'caption':'菜單3', parent_id:None},{'id':4, 'caption':'菜單1-1', parent_id:1},]{1:{'id':1, 'caption':'菜單1', parent_id:None,status:False,opened:False,child:[]},2:{'id':2, 'caption':'菜單2', parent_id:None,status:False,opened:False,child:[]},3:{'id':3, 'caption':'菜單3', parent_id:None,status:False,opened:False,child:[]},5:{'id':4, 'caption':'菜單1-1', parent_id:1,status:False,opened:False,child:[]},}"""user = models.User.objects.filter(username='alex').first()role_list = models.Role.objects.filter(users__user=user)permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__id','permission__url','permission__menu_id','permission__caption').distinct()"""[{'permission__url':'/order.html','permission__caption': '訂單管理','permission__menu_id': 1 },{'permission__url':'/order.html','permission__caption': '訂單管理','permission__menu_id': 2 },{'permission__url':'/order.html','permission__caption': '訂單管理','permission__menu_id': 3 },{'permission__url':'/order.html','permission__caption': '訂單管理','permission__menu_id': 4 },]"""##### 將權限掛靠到菜單上 ########all_menu_dict = {}for row in all_menu_list:row['child'] = [] # 添加孩子row['status'] = False # 是否顯示菜單row['opened'] = False # 是否默認打開all_menu_dict[row['id']] = rowfor per in permission_list:if not per['permission__menu_id']:continueitem = {'id': per['permission__id'],'caption': per['permission__caption'],'parent_id': per['permission__menu_id'],'url': per['permission__url'],'status': True,'opened': False}# print(item["url"])if re.match(per['permission__url'],request.path_info):# if re.match(per['permission__url'], "/orders.html"):item['opened'] = Truepid = item['parent_id']all_menu_dict[pid]['child'].append(item)# 將當前權限前輩status=Truetemp = pid # 1.父親IDwhile not all_menu_dict[temp]['status']:all_menu_dict[temp]['status'] = Truetemp = all_menu_dict[temp]['parent_id']if not temp:break# 將當前權限前輩opened=Trueif item['opened']:temp1 = pid # 1.父親IDwhile not all_menu_dict[temp1]['opened']:all_menu_dict[temp1]['opened'] = Truetemp1 = all_menu_dict[temp1]['parent_id']if not temp1:break# ############ 處理菜單和菜單之間的等級關系 ############"""all_menu_dict = {1:{'id':1, 'caption':'菜單1', parent_id:None,status:False,opened:False,child:[{'permission__url':'/order.html','permission__caption': '訂單管理','permission__menu_id': 1 },]},2:{'id':2, 'caption':'菜單2', parent_id:None,status:False,opened:False,child:[]},3:{'id':3, 'caption':'菜單3', parent_id:None,status:False,opened:False,child:[]},5:{'id':4, 'caption':'菜單1-1', parent_id:1,status:False,opened:False,child:[]},}all_menu_list= [{'id':1, 'caption':'菜單1', parent_id:None,status:False,opened:False,child:[{'permission__url':'/order.html','permission__caption': '訂單管理','permission__menu_id': 1 }, {'id':4, 'caption':'菜單1-1', parent_id:1,status:False,opened:False,child:[]},]},{'id':2, 'caption':'菜單2', parent_id:None,status:False,opened:False,child:[]},{'id':3, 'caption':'菜單3', parent_id:None,status:False,opened:False,child:[]},]"""result = []for row in all_menu_list:pid = row['parent_id']if pid:all_menu_dict[pid]['child'].append(row)else:result.append(row)##################### 結構化處理結果 ###################### print(result)# for row in result:# # print(row['caption'], row['status'], row['opened'], )# print(row)##################### 通過結構化處理結果,生成菜單開始 #####################def menu_tree(menu_list):tpl1 = """<div class='menu-item'><div class='menu-header'>{0}</div><div class='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# menu: 菜單,權限(url)if menu.get('url'):# 權限menu_str += tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])else:# 菜單if menu['child']:child_html = menu_tree(menu['child'])else:child_html = ""menu_str += tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')return menu_strmenu_html = menu_tree(result)return render(request, "menu_html.html", {"menu_html":menu_html}) app02/views.py <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><style>.menu-body{margin-left: 20px;}.menu-body a{display: block;}.menu-body a.active{color: red;}.hide{display: none;}</style> </head> <body>{{ menu_html|safe }}<script src="/static/jquery-3.2.1.js"></script><script>$(function () {$(".menu-header").click(function () {$(this).next().removeClass("hide").parent().siblings().find(".menu-body").addClass("hide")})})</script> </body> </html> menu_htmlc.result 結果遞歸 debug調試
result =[{'opened': True, 'parent_id': None, 'caption': '菜單一', 'id': 1, 'status': True,'child': [{'opened': False, 'parent_id': 1, 'caption': '帥哥管理', 'id': 5, 'status': True, 'url': '/shuaige.html'},{'opened': True, 'parent_id': 1, 'caption': '菜單一 一', 'id': 4, 'status': True,'child': [{'opened': True, 'parent_id': 4, 'caption': '用戶管理', 'id': 1, 'status': True, 'url': '/users.html'},{'opened': False, 'parent_id': 4, 'caption': '訂單管理', 'id': 2, 'status': True, 'url': '/orders.html'}]},{'opened': False, 'parent_id': 1, 'caption': '菜單一 二', 'id': 5, 'status': False, 'child': []},{'opened': False, 'parent_id': 1, 'caption': '菜單一 三', 'id': 6, 'status': False, 'child': []}]},{'opened': False, 'parent_id': None, 'caption': '菜單二', 'id': 2, 'status': False, 'child': []},{'opened': False, 'parent_id': None, 'caption': '菜單三', 'id': 3, 'status': False, 'child': []}] result = [{'opened': True, 'parent_id': None, 'caption': '菜單一', 'id': 1, 'status': True, 'child': [{'opened': False, 'parent_id': 1, 'caption': '帥哥管理', 'id': 5, 'status': True, 'url': '/shuaige.html'}, {'opened': True, 'parent_id': 1, 'caption': '菜單一 一', 'id': 4, 'status': True, 'child': [{'opened': True, 'parent_id': 4, 'caption': '用戶管理', 'id': 1, 'status': True, 'url': '/users.html'}, {'opened': False, 'parent_id': 4, 'caption': '訂單管理', 'id': 2, 'status': True, 'url': '/orders.html'}]}, {'opened': False, 'parent_id': 1, 'caption': '菜單一 二', 'id': 5, 'status': False, 'child': []}, {'opened': False, 'parent_id': 1, 'caption': '菜單一 三', 'id': 6, 'status': False, 'child': []}]}, {'opened': False, 'parent_id': None, 'caption': '菜單二', 'id': 2, 'status': False, 'child': []}, {'opened': False, 'parent_id': None, 'caption': '菜單三', 'id': 3, 'status': False, 'child': []}]def menu_tree(menu_list):tpl1 = """<div class='menu-item'><div class='menu-header'>{0}</div><div class='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# menu: 菜單,權限(url)if menu.get('url'):# 權限menu_str += tpl2.format(menu['url'], 'active' if menu['opened'] else "", menu['caption'])print("***", menu_str)else:# 菜單if menu['child']:child_html = menu_tree(menu['child'])print("----", child_html)else:child_html = ""print("111")menu_str += tpl1.format(menu['caption'], child_html, "" if menu['opened'] else 'hide')print("AAAAA", menu_str)print(123)return menu_strmenu_html = menu_tree(result) print("xx", menu_html)二、組件
生成公共app- 權限限制- 生成菜單 python3 manage.py startapp rbac配置文件:
復制代碼 #白名單url, 不驗證 VALID_URL = ['/app01/.*','/app02/.*''/login.html''/logout.html' ] config.pyrbac models:
from django.db import modelsclass User(models.Model):"""用戶表"""username = models.CharField(verbose_name='用戶名', max_length=32)password = models.CharField(verbose_name='密碼', max_length=64)email = models.EmailField(verbose_name='郵箱')def __str__(self):return self.usernameclass Role(models.Model):"""角色表"""caption = models.CharField(verbose_name='角色', max_length=32)def __str__(self):return self.captionclass User2Role(models.Model):"""用戶角色關系表"""user = models.ForeignKey(User, verbose_name='用戶', related_name='roles')role = models.ForeignKey(Role, verbose_name='角色', related_name='users')def __str__(self):return '%s-%s' % (self.user.username, self.role.caption,)class Menu(models.Model):"""菜單表"""caption = models.CharField(verbose_name='菜單名稱', max_length=32)parent = models.ForeignKey('self', verbose_name='父菜單', related_name='p', null=True, blank=True)def __str__(self):prev = ""parent = self.parentwhile True:if parent:prev = prev + '-' + str(parent.caption)parent = parent.parentelse:breakreturn '%s-%s' % (prev, self.caption,)class Permission(models.Model):"""權限"""caption = models.CharField(verbose_name='權限', max_length=32)url = models.CharField(verbose_name='URL正則', max_length=128)menu = models.ForeignKey(Menu, verbose_name='所屬菜單', related_name='permissions',null=True,blank=True)def __str__(self):return "%s-%s" % (self.caption, self.url,)class Action(models.Model):"""操作:增刪改查"""caption = models.CharField(verbose_name='操作標題', max_length=32)code = models.CharField(verbose_name='方法', max_length=32)def __str__(self):return self.captionclass Permission2Action2Role(models.Model):"""權限操作關系表"""permission = models.ForeignKey(Permission, verbose_name='權限URL', related_name='actions')action = models.ForeignKey(Action, verbose_name='操作', related_name='permissions')role = models.ForeignKey(Role, verbose_name='角色', related_name='p2as')class Meta:unique_together = (('permission', 'action', 'role'),)def __str__(self):return "%s-%s-%s" % (self.permission, self.action, self.role,) models.py中間件認證
#驗證中間件from django.utils.deprecation import MiddlewareMixin from django.shortcuts import HttpResponse from rbac import config import reclass RbacMiddleware(MiddlewareMixin):def process_request(self,request,*args,**kwargs):for pattern in config.VALID_URL:if re.match(pattern,request.path_info):return Noneaction = request.GET.get('md') # GETuser_permission_dict = request.session.get('user_permission_dict')if not user_permission_dict:return HttpResponse('無權限')# action_list = user_permission_dict.get(request.path_info)flag = Falsefor k,v in user_permission_dict.items():if re.match(k,request.path_info):if action in v:flag = Truebreakif not flag:return HttpResponse('無權限') /middleware/md.py后端代碼
import re from rbac import models from django.utils.safestring import mark_safedef permission_session(user_id,request):""":param user_id: rbac中的user表中一條數據id:param request::return:"""# obj = models.User.objects.filter(username='楊明').first()# # # x = models.User2Role.objects.filter(user_id=obj.id)# # [User2Role,User2Role,User2Role]# # role_list = models.Role.objects.filter(users__user_id=obj.id)# # [Role,]# from django.db.models import Count# # permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').annotate(c=Count('id'))# permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').distinct()"""[{permission_url: '/index.html', action_code:'GET'},{permission_url: '/index.html', action_code:'POST'},{permission_url: '/index.html', action_code:'DEL'},{permission_url: '/index.html', action_code:'Edit'},{permission_url: '/order.html', action_code:'GET'},{permission_url: '/order.html', action_code:'POST'},{permission_url: '/order.html', action_code:'DEL'},{permission_url: '/order.html', action_code:'Edit'},]放在Session中/index.html?md=GET{'/index.html': [GET,POST,DEL,Edit],'/order.html': [GET,POST,DEL,Edit],}"""user_permission_dict = {'/ah-index.html': ["GET","POST","DEL","Edit"],'/order.html': ["GET","POST","DEL","Edit"],'/index-(\d+).html': ["GET","POST","DEL","Edit"],}request.session['user_permission_dict'] = user_permission_dictdef menu(user_id,current_url):"""根據用戶ID,當前URL:獲取用戶所有菜單以及權限,是否顯示,是否打開:param user_id::param current_url::return:"""# 所有菜單:處理成當前用關聯的菜單all_menu_list = models.Menu.objects.all().values('id','caption','parent_id')user = models.User.objects.filter(id=user_id).first()role_list = models.Role.objects.filter(users__user=user)permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__id','permission__url','permission__menu_id','permission__caption').distinct()##### 將權限掛靠到菜單上 ########all_menu_dict = {}for row in all_menu_list:row['child'] = [] # 添加孩子row['status'] = False # 是否顯示菜單row['opened'] = False # 是否默認打開all_menu_dict[row['id']] = rowfor per in permission_list:if not per['permission__menu_id']:continueitem = {'id':per['permission__id'],'caption':per['permission__caption'],'parent_id':per['permission__menu_id'],'url': per['permission__url'],'status': True,'opened': False}if re.match(per['permission__url'],current_url):item['opened'] = Truepid = item['parent_id']all_menu_dict[pid]['child'].append(item)# 將當前權限前輩status=Truetemp = pid # 1.父親IDwhile not all_menu_dict[temp]['status']:all_menu_dict[temp]['status'] = Truetemp = all_menu_dict[temp]['parent_id']if not temp:break# 將當前權限前輩opened=Trueif item['opened']:temp1 = pid # 1.父親IDwhile not all_menu_dict[temp1]['opened']:all_menu_dict[temp1]['opened'] = Truetemp1 = all_menu_dict[temp1]['parent_id']if not temp1: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)##################### 結構化處理結果 #####################for row in result:print(row['caption'],row['status'],row['opened'],row)def menu_tree(menu_list):tpl1 = """<div class='menu-item'><div class='menu-header'>{0}</div><div class='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# menu: 菜單,權限(url)if menu.get('url'):# 權限menu_str += tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])else:# 菜單if menu['child']:child_html = menu_tree(menu['child'])else:child_html = ""menu_str += tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')return menu_strmenu_html = menu_tree(result)return menu_html# simple_tag def css():v = """<style>.hide{display: none;}.menu-body{margin-left: 20px;}.menu-body a{display: block;}.menu-body a.active{color: red;}</style>"""return v# simple_tag def js():v = """<script>$(function(){$('.menu-header').click(function(){$(this).next().removeClass('hide').parent().siblings().find('.menu-body').addClass('hide');})})</script>"""return v service.pya:封裝以后調用
加入中間件
#自定義的中間件加入到setting MIDDLEWARE = ['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','rbac.middleware.md.RbacMiddleware', ] setting.py #后端 from rbac import service1. 用戶登錄后,拿到用戶的ID,調用permission_session()函數(函數代碼還沒寫)函數獲取用戶角色的權限,格式如:“”“user_permission_dict = {'/ah-index.html': ["GET","POST","DEL","Edit"],'/order.html': ["GET","POST","DEL","Edit"],'/index-(\d+).html': ["GET","POST","DEL","Edit"],}”“”def login():permission_session(用戶ID,request)return .....2.setting中加入中間件,如上3.#獲取菜單current_url= request.pathinfomenu_list = service.menu(用戶ID,current_url)4.盡量用simple_tagcss = servicr.css()js = servicr.js()5.前端{{ css|safe }}{{ menu_list|safe }}{{ js|safe }}
?
轉載于:https://www.cnblogs.com/bingabcd/p/7508141.html
總結
- 上一篇: EcOS安装
- 下一篇: odoo tree视图 当页不弹窗显示