使用Django框架怎么實(shí)現(xiàn)一個(gè)rbac權(quán)限組件?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
1.基于rbac的權(quán)限管理
RBAC(Role-Based Access Control,基于角色的訪問控制),就是用戶通過角色與權(quán)限進(jìn)行關(guān)聯(lián)。簡(jiǎn)單地說,一個(gè)用戶擁有若干角色,一個(gè)角色擁有若干權(quán)限。這樣,就構(gòu)造成“用戶-角色-權(quán)限”的授權(quán)模型。在這種模型中,用戶與角色之間,角色與權(quán)限之間都是多對(duì)多的關(guān)系。
簡(jiǎn)單的模型圖示如下:
2.Rbac組件的基本目錄結(jié)構(gòu):
3.按照寫的流程,來講解rbac組件中的各個(gè)部分,以及功能,
3.1 models數(shù)據(jù)庫表設(shè)計(jì)(models.py)。
為了在前端頁面實(shí)現(xiàn)2方面的控制,還需要引入兩個(gè)表菜單menu和分組group:1.在一個(gè)頁面,當(dāng)前用戶的權(quán)限,例如是否顯示添加按鈕、編輯、刪除等按鈕;2.左側(cè)菜單欄的創(chuàng)建。所以一共是5個(gè)類,7張表,詳細(xì)model請(qǐng)看下邊代碼。
models.py
# models.py from django.db import models class Menu(models.Model): '''頁面中的菜單名''' title = models.CharField(max_length=32) class Group(models.Model): '''權(quán)限url所屬的組''' caption = models.CharField(verbose_name='組名稱',max_length=32) menu =models.ForeignKey(verbose_name='組所屬菜單',to='Menu',default=1) # 組所在的菜單 class Meta: verbose_name_plural = 'Group組表' def __str__(self): return self.caption class User(models.Model): """ 用戶表 """ username = models.CharField(verbose_name='用戶名',max_length=32) password = models.CharField(verbose_name='密碼',max_length=64) email = models.CharField(verbose_name='郵箱',max_length=32) roles = models.ManyToManyField(verbose_name='具有的所有角色',to="Role",blank=True) class Meta: verbose_name_plural = "用戶表" def __str__(self): return self.username class Role(models.Model): """ 角色表 """ title = models.CharField(max_length=32) permissions = models.ManyToManyField(verbose_name='具有的所有權(quán)限',to='Permission',blank=True) class Meta: verbose_name_plural = "角色表" def __str__(self): return self.title class Permission(models.Model): """ 權(quán)限表 """ title = models.CharField(verbose_name='標(biāo)題',max_length=32) url = models.CharField(verbose_name="含正則URL",max_length=64) is_menu = models.BooleanField(verbose_name="是否是菜單") code = models.CharField(verbose_name='url代碼',max_length=32,default=0) # 路徑對(duì)應(yīng)的描述名稱 group = models.ForeignKey(verbose_name='所屬組',to='Group',null=True,blank=True) # 所屬組 class Meta: verbose_name_plural = "權(quán)限表" def __str__(self): return self.titlemodel
3.2 service中的init_permission.py
功能:在用戶登錄成功的時(shí)候,在session中寫入兩個(gè)內(nèi)容:1.拿到當(dāng)前用戶的權(quán)限url(code信息);2.拿到當(dāng)前用戶的可以做菜單的url信息。
詳細(xì)代碼如下:
初始化權(quán)限
def init_permission(user, request): ''' 前端頁面調(diào)用,把當(dāng)前登錄用戶的權(quán)限放到session中,request參數(shù)指前端傳入的當(dāng)前當(dāng)前l(fā)ogin請(qǐng)求時(shí)的request :param user: 當(dāng)前登錄用戶 :param request: 當(dāng)前請(qǐng)求 :return: None ''' # 拿到當(dāng)前用戶的權(quán)限信息 permission_url_list = user.roles.values('permissions__group_id', 'permissions__code', 'permissions__url', 'permissions__group__menu__id', # 菜單需要 'permissions__group__menu__title', # 菜單需要 'permissions__title', # 菜單需要 'permissions__url', # 菜單需要 'permissions__is_menu', # 菜單需要 ).distinct() # 頁面顯示權(quán)限相關(guān),用到了權(quán)限的分組, dest_dic = {} for each in permission_url_list: if each['permissions__group_id'] in dest_dic: dest_dic[each['permissions__group_id']]['code'].append(each['permissions__code']) dest_dic[each['permissions__group_id']]['per_url'].append(each['permissions__url']) else: # 剛循環(huán),先創(chuàng)建需要的結(jié)構(gòu),并把第一次的值放進(jìn)去。 dest_dic[each['permissions__group_id']] = {'code': [each['permissions__code'], ], 'per_url': [each['permissions__url'], ]} request.session['permission_url_list'] = dest_dic # 頁面菜單相關(guān) # 1.去掉不做菜單的url,拿到的結(jié)果是menu_list,列表中的元素是字典 menu_list = [] for item_dic in permission_url_list: if item_dic['permissions__is_menu']: temp = {'menu_id':item_dic['permissions__group__menu__id'], 'menu_title':item_dic['permissions__group__menu__title'], 'permission__title': item_dic['permissions__title'], 'permission_url':item_dic['permissions__url'], 'permissions__is_menu':item_dic['permissions__is_menu'], 'active':False, # 用于頁面是否被選中, } # temp 其實(shí)只是給key重新起名字,之前的名字太長(zhǎng)了。。。。 menu_list.append(temp) # 執(zhí)行完成之后是如下的數(shù)據(jù),用來做菜單。 request.session['permission_menu_list'] = menu_list
3.3 中間件md
功能:1.白名單驗(yàn)證;
2.驗(yàn)證是否已經(jīng)寫入session,即:是否已經(jīng)登錄;
3.當(dāng)前訪問的url與當(dāng)前用戶的權(quán)限url進(jìn)行匹配驗(yàn)證,并在request中寫入code信息,
詳細(xì)代碼如下:
中間件
import re from django.shortcuts import render,redirect,HttpResponse from django.conf import settings class MiddlewareMixin(object): def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__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 M1(MiddlewareMixin): ''' 判斷用戶有無此url的權(quán)限的中間件 ''' def process_request(self,request): current_url = request.path_info # 1.白名單驗(yàn)證 valid_url = settings.VALID_URL for each in valid_url: if re.match(each, current_url): return None # 2.驗(yàn)證是否已經(jīng)寫入session,即:是否已經(jīng)登錄 permission_dic = request.session.get('permission_url_list') if not permission_dic: return redirect('/login/') # 3.與當(dāng)前訪問的url與權(quán)限url進(jìn)行匹配驗(yàn)證,并在request中寫入code信息, flag = False for group_id,code_urls in permission_dic.items(): for url in code_urls['per_url']: regax = '^{0}$'.format(url) if re.match(regax,current_url): flag = True request.permission_code_list = code_urls['code'] # 在session中增加code的信息,用于在頁面判斷在當(dāng)前頁面的權(quán)限, break if flag: break if not flag: return HttpResponse('無權(quán)訪問') def process_response(self,request,response): return response
3.4 左側(cè)菜單的生成templatetags目錄下的rbac.py
功能;生成頁面中的左側(cè)菜單用inclusion_tag標(biāo)簽
運(yùn)用:我們只需要在需要用到的文件中引用就可以生成這個(gè)菜單部分的內(nèi)容。
需要用到的模板文件中:
{% load rbac %}
{% menu_html request %} 這部分就會(huì)變成用inclusion_tag生成的menu_html
詳細(xì)代碼如下:
inclusion_tag生成左側(cè)菜單
import re from django.template import Library register = Library() # inclusion_tag的結(jié)果是:把menu_html函數(shù)的返回值,放到menu_html中做渲染,生成一個(gè)渲染之后的大字符串, # 在前端需要顯示這個(gè)字符串的地方,只要調(diào)用menu_html就可以,如果有菜單需要傳參數(shù),這里是request,前端模板本來就有request, @register.inclusion_tag('menu.html') def menu_html(request): current_url = request.path_info # 結(jié)構(gòu)化在頁面顯示的menu數(shù)據(jù) menu_list = request.session.get('permission_menu_list') menu_show_dic = {} for item in menu_list: # 先跟當(dāng)前url進(jìn)行匹配,如果當(dāng)前的url在權(quán)限URl中,則需要修改當(dāng)前的active,用于在前端頁面的顯示。 url = item['permission_url'] reg = '^{0}$'.format(url) if re.match(reg, current_url): print('匹配到了') item['active'] = True if item['menu_id'] in menu_show_dic: menu_show_dic[item['menu_id']]['children'].append( {'permission__title': item['permission__title'], 'permission_url': item['permission_url'], 'active': item['active']}) if item['active']: menu_show_dic[item['menu_id']]['active'] = True else: menu_show_dic[item['menu_id']] = {'menu_id': item['menu_id'], 'menu_title': item['menu_title'], 'active': False, 'children': [{'permission__title': item['permission__title'], 'permission_url': item['permission_url'], 'active': item['active']}, ] } if item['active']: menu_show_dic[item['menu_id']]['active'] = True return {'menu_dic':menu_show_dic}
需要的模板文件templates下的menu.html
menu.html
# menu.html <div class="menu"> {% for k,menu in menu_dic.items %} {# 一級(jí)菜單 #} <div class="menu_first">{{ menu.menu_title }}</div> {# 二級(jí)菜單(就是一級(jí)菜單下邊的內(nèi)容) #} {% if menu.active %} <ul class=""> {% else %} <ul class="hide"> {% endif %} {% for child in menu.children %} {% if child.active %} <li class="menu_second active"><a href="{{ child.permission_url }}" rel="external nofollow" rel="external nofollow" >{{ child.permission__title }}</a></li> {% else %} <li class="menu_second"><a href="{{ child.permission_url }}" rel="external nofollow" rel="external nofollow" >{{ child.permission__title }}</a></li> {% endif %} {% endfor %} </ul> {% endfor %} </div>
使用inclusion_tag的文件示例:
inclusion_tag的使用模板文件
# 這個(gè)是django的模板文件 {% load rbac %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}模板{% endblock %}</title> <link rel="stylesheet" href="{% static 'rbac/bootstrap-3.3.7/css/bootstrap.min.css' %}" rel="external nofollow" > <link rel="stylesheet" href="{% static 'rbac/menu.css' %}" rel="external nofollow" > {% block css %} {% endblock css %} </head> <body> <div class="container-fluid"> <div class="row"> <div class="col-md-2 menu"> {% block menu %} {% menu_html request %} {# 用inclusion_tag生成的menu_html #} {% endblock menu %} </div> <div class="col-md-9"> {% block content %} content {% endblock %} </div> </div> </div>
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)的支持。
網(wǎng)頁標(biāo)題:使用Django框架怎么實(shí)現(xiàn)一個(gè)rbac權(quán)限組件-創(chuàng)新互聯(lián)
本文網(wǎng)址:http://jinyejixie.com/article0/djcpoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄、小程序開發(fā)、自適應(yīng)網(wǎng)站、網(wǎng)站建設(shè)、App開發(fā)、網(wǎng)站設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容