1.基於rbac的權限管理css
RBAC(Role-Based Access Control,基於角色的訪問控制),就是用戶經過角色與權限進行關聯。簡單地說,一個用戶擁有若干角色,一個角色擁有若干權限。這樣,就構形成「用戶-角色-權限」的受權模型。在這種模型中,用戶與角色之間,角色與權限之間都是多對多的關係。html
簡單的模型圖示以下:前端
2.Rbac組件的基本目錄結構:數據庫
3.按照寫的流程,來說解rbac組件中的各個部分,以及功能,django
3.1 models數據庫表設計(models.py)。bootstrap
爲了在前端頁面實現2方面的控制,還須要引入兩個表菜單menu和分組group:1.在一個頁面,當前用戶的權限,例如是否顯示添加按鈕、編輯、刪除等按鈕;2.左側菜單欄的建立。因此一共是5個類,7張表,詳細model請看下邊代碼。session
1 # models.py 2 3 from django.db import models 4 5 6 class Menu(models.Model): 7 '''頁面中的菜單名''' 8 title = models.CharField(max_length=32) 9 10 class Group(models.Model): 11 '''權限url所屬的組''' 12 caption = models.CharField(verbose_name='組名稱',max_length=32) 13 menu =models.ForeignKey(verbose_name='組所屬菜單',to='Menu',default=1) # 組所在的菜單 14 15 class Meta: 16 verbose_name_plural = 'Group組表' 17 18 def __str__(self): 19 return self.caption 20 21 class User(models.Model): 22 """ 23 用戶表 24 """ 25 username = models.CharField(verbose_name='用戶名',max_length=32) 26 password = models.CharField(verbose_name='密碼',max_length=64) 27 email = models.CharField(verbose_name='郵箱',max_length=32) 28 29 roles = models.ManyToManyField(verbose_name='具備的全部角色',to="Role",blank=True) 30 class Meta: 31 verbose_name_plural = "用戶表" 32 33 def __str__(self): 34 return self.username 35 36 class Role(models.Model): 37 """ 38 角色表 39 """ 40 title = models.CharField(max_length=32) 41 permissions = models.ManyToManyField(verbose_name='具備的全部權限',to='Permission',blank=True) 42 class Meta: 43 verbose_name_plural = "角色表" 44 45 def __str__(self): 46 return self.title 47 48 49 class Permission(models.Model): 50 """ 51 權限表 52 """ 53 title = models.CharField(verbose_name='標題',max_length=32) 54 url = models.CharField(verbose_name="含正則URL",max_length=64) 55 is_menu = models.BooleanField(verbose_name="是不是菜單") 56 57 code = models.CharField(verbose_name='url代碼',max_length=32,default=0) # 路徑對應的描述名稱 58 group = models.ForeignKey(verbose_name='所屬組',to='Group',null=True,blank=True) # 所屬組 59 60 class Meta: 61 verbose_name_plural = "權限表" 62 63 def __str__(self): 64 return self.titlemodel
3.2 service中的init_permission.py app
功能:在用戶登陸成功的時候,在session中寫入兩個內容:1.拿到當前用戶的權限url(code信息);2.拿到當前用戶的能夠作菜單的url信息。
ide
詳細代碼以下:函數
1 def init_permission(user, request): 2 ''' 3 前端頁面調用,把當前登陸用戶的權限放到session中,request參數指前端傳入的當前當前login請求時的request 4 :param user: 當前登陸用戶 5 :param request: 當前請求 6 :return: None 7 ''' 8 # 拿到當前用戶的權限信息 9 permission_url_list = user.roles.values('permissions__group_id', 10 'permissions__code', 11 'permissions__url', 12 'permissions__group__menu__id', # 菜單須要 13 'permissions__group__menu__title', # 菜單須要 14 'permissions__title', # 菜單須要 15 'permissions__url', # 菜單須要 16 'permissions__is_menu', # 菜單須要 17 ).distinct() 18 19 20 # 頁面顯示權限相關,用到了權限的分組, 21 dest_dic = {} 22 for each in permission_url_list: 23 if each['permissions__group_id'] in dest_dic: 24 dest_dic[each['permissions__group_id']]['code'].append(each['permissions__code']) 25 dest_dic[each['permissions__group_id']]['per_url'].append(each['permissions__url']) 26 else: 27 # 剛循環,先建立須要的結構,並把第一次的值放進去。 28 dest_dic[each['permissions__group_id']] = {'code': [each['permissions__code'], ], 29 'per_url': [each['permissions__url'], ]} 30 31 request.session['permission_url_list'] = dest_dic 32 33 # 頁面菜單相關 34 # 1.去掉不作菜單的url,拿到的結果是menu_list,列表中的元素是字典 35 menu_list = [] 36 for item_dic in permission_url_list: 37 if item_dic['permissions__is_menu']: 38 temp = {'menu_id':item_dic['permissions__group__menu__id'], 39 'menu_title':item_dic['permissions__group__menu__title'], 40 'permission__title': item_dic['permissions__title'], 41 'permission_url':item_dic['permissions__url'], 42 'permissions__is_menu':item_dic['permissions__is_menu'], 43 'active':False, # 用於頁面是否被選中, 44 } 45 # temp 其實只是給key從新起名字,以前的名字太長了。。。。 46 menu_list.append(temp) 47 # 執行完成以後是以下的數據,用來作菜單。 48 49 request.session['permission_menu_list'] = menu_list
3.3 中間件md
功能:1.白名單驗證;
2.驗證是否已經寫入session,即:是否已經登陸;
3.當前訪問的url與當前用戶的權限url進行匹配驗證,並在request中寫入code信息,
詳細代碼以下:
1 import re 2 from django.shortcuts import render,redirect,HttpResponse 3 from django.conf import settings 4 5 class MiddlewareMixin(object): 6 def __init__(self, get_response=None): 7 self.get_response = get_response 8 super(MiddlewareMixin, self).__init__() 9 10 def __call__(self, request): 11 response = None 12 if hasattr(self, 'process_request'): 13 response = self.process_request(request) 14 if not response: 15 response = self.get_response(request) 16 if hasattr(self, 'process_response'): 17 response = self.process_response(request, response) 18 return response 19 20 class M1(MiddlewareMixin): 21 ''' 22 判斷用戶有無此url的權限的中間件 23 ''' 24 def process_request(self,request): 25 current_url = request.path_info 26 27 # 1.白名單驗證 28 valid_url = settings.VALID_URL 29 for each in valid_url: 30 if re.match(each, current_url): 31 return None 32 33 # 2.驗證是否已經寫入session,即:是否已經登陸 34 permission_dic = request.session.get('permission_url_list') 35 if not permission_dic: 36 return redirect('/login/') 37 38 # 3.與當前訪問的url與權限url進行匹配驗證,並在request中寫入code信息, 39 flag = False 40 for group_id,code_urls in permission_dic.items(): 41 for url in code_urls['per_url']: 42 regax = '^{0}$'.format(url) 43 if re.match(regax,current_url): 44 flag = True 45 request.permission_code_list = code_urls['code'] # 在session中增長code的信息,用於在頁面判斷在當前頁面的權限, 46 break 47 if flag: 48 break 49 50 if not flag: 51 return HttpResponse('無權訪問') 52 53 54 def process_response(self,request,response): 55 return response
3.4 左側菜單的生成templatetags目錄下的rbac.py
功能;生成頁面中的左側菜單用inclusion_tag標籤
運用:咱們只須要在須要用到的文件中引用就能夠生成這個菜單部分的內容。
須要用到的模板文件中:
{% load rbac %}
{% menu_html request %} 這部分就會變成用inclusion_tag生成的menu_html
詳細代碼以下:
1 import re 2 3 from django.template import Library 4 5 register = Library() 6 7 # inclusion_tag的結果是:把menu_html函數的返回值,放到menu_html中作渲染,生成一個渲染以後的大字符串, 8 # 在前端須要顯示這個字符串的地方,只要調用menu_html就能夠,若是有菜單須要傳參數,這裏是request,前端模板原本就有request, 9 @register.inclusion_tag('menu.html') 10 def menu_html(request): 11 current_url = request.path_info 12 13 # 結構化在頁面顯示的menu數據 14 menu_list = request.session.get('permission_menu_list') 15 16 menu_show_dic = {} 17 for item in menu_list: 18 # 先跟當前url進行匹配,若是當前的url在權限URl中,則須要修改當前的active,用於在前端頁面的顯示。 19 url = item['permission_url'] 20 reg = '^{0}$'.format(url) 21 if re.match(reg, current_url): 22 print('匹配到了') 23 item['active'] = True 24 25 if item['menu_id'] in menu_show_dic: 26 menu_show_dic[item['menu_id']]['children'].append( 27 {'permission__title': item['permission__title'], 'permission_url': item['permission_url'], 28 'active': item['active']}) 29 if item['active']: 30 menu_show_dic[item['menu_id']]['active'] = True 31 else: 32 menu_show_dic[item['menu_id']] = {'menu_id': item['menu_id'], 33 'menu_title': item['menu_title'], 34 'active': False, 35 'children': [{'permission__title': item['permission__title'], 36 'permission_url': item['permission_url'], 37 'active': item['active']}, ] 38 } 39 if item['active']: 40 menu_show_dic[item['menu_id']]['active'] = True 41 42 43 return {'menu_dic':menu_show_dic}
須要的模板文件templates下的menu.html
1 # menu.html 2 3 <div class="menu"> 4 {% for k,menu in menu_dic.items %} 5 {# 一級菜單 #} 6 <div class="menu_first">{{ menu.menu_title }}</div> 7 8 {# 二級菜單(就是一級菜單下邊的內容) #} 9 {% if menu.active %} 10 <ul class=""> 11 {% else %} 12 <ul class="hide"> 13 {% endif %} 14 15 {% for child in menu.children %} 16 {% if child.active %} 17 <li class="menu_second active"><a href="{{ child.permission_url }}">{{ child.permission__title }}</a></li> 18 {% else %} 19 <li class="menu_second"><a href="{{ child.permission_url }}">{{ child.permission__title }}</a></li> 20 {% endif %} 21 {% endfor %} 22 </ul> 23 {% endfor %} 24 </div>
使用inclusion_tag的文件示例:
1 # 這個是django的模板文件 2 {% load rbac %} 3 4 <!DOCTYPE html> 5 <html lang="en"> 6 <head> 7 <meta charset="UTF-8"> 8 <title>{% block title %}模板{% endblock %}</title> 9 <link rel="stylesheet" href="{% static 'rbac/bootstrap-3.3.7/css/bootstrap.min.css' %}"> 10 <link rel="stylesheet" href="{% static 'rbac/menu.css' %}"> 11 {% block css %} {% endblock css %} 12 13 </head> 14 <body> 15 <div class="container-fluid"> 16 <div class="row"> 17 <div class="col-md-2 menu"> 18 {% block menu %} 19 {% menu_html request %} {# 用inclusion_tag生成的menu_html #} 20 {% endblock menu %} 21 </div> 22 <div class="col-md-9"> 23 {% block content %} 24 content 25 {% endblock %} 26 </div> 27 </div> 28 </div>
以上就是django中基於rbac實現的權限組件