基于Django框架的權(quán)限組件rbac實(shí)例講解
1.基于rbac的權(quán)限管理
RBAC(Role-Based Access Control,基于角色的訪問控制),就是用戶通過角色與權(quán)限進(jìn)行關(guān)聯(lián)。簡單地說,一個(gè)用戶擁有若干角色,一個(gè)角色擁有若干權(quán)限。這樣,就構(gòu)造成“用戶-角色-權(quán)限”的授權(quán)模型。在這種模型中,用戶與角色之間,角色與權(quán)限之間都是多對多的關(guā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請看下邊代碼。
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) # 路徑對應(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請求時(shí)的request
:param user: 當(dāng)前登錄用戶
:param request: 當(dā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重新起名字,之前的名字太長了。。。。
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 %} 這部分就會變成用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 %}
{# 一級菜單 #}
<div class="menu_first">{{ menu.menu_title }}</div>
{# 二級菜單(就是一級菜單下邊的內(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>
以上就是django中基于rbac實(shí)現(xiàn)的權(quán)限組件
這篇基于Django框架的權(quán)限組件rbac實(shí)例講解就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解pytest+Allure搭建方法以及生成報(bào)告常用操作
本文主要介紹了詳解pytest+Allure搭建方法以及生成報(bào)告常用操作,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Python使用pyinstaller打包含有g(shù)ettext?locales語言環(huán)境的項(xiàng)目(推薦)
最近在用 pyhton 做一個(gè)圖片處理的小工具,順便接觸了gettext,用來實(shí)現(xiàn)本地化化中英文轉(zhuǎn)換,本文通過一個(gè)項(xiàng)目給大家詳細(xì)介紹下,感興趣的朋友跟隨小編一起看看吧2022-01-01
Numpy 多維數(shù)據(jù)數(shù)組的實(shí)現(xiàn)
這篇文章主要介紹了Numpy 多維數(shù)據(jù)數(shù)組的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
python將一組數(shù)分成每3個(gè)一組的實(shí)例
今天小編就為大家分享一篇python將一組數(shù)分成每3個(gè)一組的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-11-11
python學(xué)生管理系統(tǒng)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了python學(xué)生管理系統(tǒng)的實(shí)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-04-04

