django--rtbac权限管理
RBAC权限管理
-
详解
基于角色的权限访问控制(Role-Based Access Control)作为传统访问控制(自主访问,强制访问)的有前景的代替受到广泛的关注。在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。角色与角色的关系可以建立起来以囊括更广泛的客观情况。 -
简介
RBAC支持三个著名的安全原则:最小权限原则,责任分离原则和数据抽象原则。
- 最小权限原则之所以被RBAC所支持,是因为RBAC可以将其角色配置成其其他完成任务所需要的最小权限集。
- 责任分离原则可以通过调用相互独立互斥的角色来共同完成敏感任务而体现
- 数据抽象可以通过权限的抽象来体现,然而这些原则必须通过RBAC各部件的详细配置才能实现RBAC有许多部件(BUCU),这使得RBAC的管理多面化。尤其是,我们要分割这些问题来讨论:用户与角色的指派;角色与权限的指派;为定义角色的继承进行的角色与角色的指派。这些活动都要求把用户和权限联系起来。然而在很多情况下它们最好由不同的管理员或管理角色来做。对角色指派权限是典型的应用管理者的职责。银行应用中,把借款、存款操作权限指派给出纳角色,把批准贷款操作权限指派给经理角色。而将具体人员指派给相应的出纳角色和管理者角色是人事管理的范畴。角色与角色的指派包含用户与角色的指派、角色与权限的指派的一些特点。更一般来说,角色与角色的关系体现了更广泛的策略。
表关系设计
五个类七个表
- Userinfo
class UserInfo(models.Model):
"""
用户表
"""
name = models.CharField(max_length=32)
password = models.CharField(max_length=64)
email = models.CharField(max_length=32)
roles = models.ManyToManyField(to='Role')
def __str__(self):
return self.name
- role
class Role(models.Model):
"""
角色
"""
title = models.CharField(max_length=32)
permissions = models.ManyToManyField(to='Permission',blank=True)
def __str__(self):
return self.title
- Permission
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(max_length=32)
url = models.CharField(max_length=128)
code = models.CharField(max_length=16,default='list')
group = models.ForeignKey(to='PermissionGroup',default=1)
# is_menu = models.BooleanField(default=False) # 权限是否可以在菜单中显示
parent = models.ForeignKey(verbose_name='组内可以作为菜单的权限',to='Permission', null=True,related_name='xx')
def __str__(self):
return self.title
- PermissionGroup
class PermissionGroup(models.Model):
"""
权限组
反向: permission_set
反向: xx
"""
caption = models.CharField(max_length=32)
menu = models.ForeignKey(to='Menu',default=1)
def __str__(self):
return self.caption
- menu
class Menu(models.Model):
"""
菜单
"""
title = models.CharField(max_length=32)
插件使用
在 django项目中新建项目
- cd rbac
- python manage.py startapp rbac
- 在主文件的setting中配置
NSTALLED_APPS = [
...
'rbac.apps.RbacConfig', #在自定义程序里 填写安装应用程序的配置
'crm.apps.CrmConfig',
]
MIDDLEWARE = [
...
'django.middleware.clickjacking.XFrameOptionsMiddleware', # 创建rbac所需的中间件配置
]
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
- 在admin里进行录入数据
- 设置该系统的白名单(setting.py)
PERMISSION_SESSION_KEY = "xxxxxxxxx"
MENU_SESSION_KEY = "sdf3sdfsdfsad"
PERMISSION_VALID_URL = [
'/login/',
'/admin/.*',
]
- 配置中间件
from django.utils.deprecation import MiddlewareMixin
import re
from django.shortcuts import HttpResponse
from django.conf import settings
class RbacMiddleware(MiddlewareMixin):
def process_request(self,request):
# 1. 获取白名单,让白名单中的所有url和当前访问url匹配
for reg in settings.PERMISSION_VALID_URL:
if re.match(reg,request.path_info):
return None
# 2. 获取权限
permission_dict = request.session.get(settings.PERMISSION_SESSION_KEY)
if not permission_dict:
return HttpResponse('未获取到当前用户的权限信息,无法访问')
"""
permission_dict={
1:{
'urls': ['/users/','/users/add/','/users/del/(\d+)/','/users/edit/(\d+)/'],
'codes':['list','add','del','edit']
},
2:{
'urls': ['/users/','/users/add/',],
'codes':['list','add',]
},
}
"""
flag = False
# 3. 对用户请求的url进行匹配
for value in permission_dict.values():
urls = value['urls']
codes = value['codes']
for reg in urls:
regx = "^%s$" % (reg,)
if re.match(regx, request.path_info):
flag = True
break
if flag:
request.permission_codes = codes
break
if not flag:
return HttpResponse('无权访问')
- 三级菜单设置
- 在rbac中新建py文件进行
from django.conf import settings
def init_permission(user,request):
"""
获取当前用户权限信息,并放入到session中。
:param user:
:param request:
:return:
"""
permission_list = user.roles.filter(permissions__title__isnull=False).values('permissions__id', # 权限ID
'permissions__title', # 权限标题
'permissions__group_id', # 权限所在的组ID
'permissions__code', # 权限代号
'permissions__parent_id', # 组内菜单id
'permissions__group__menu_id', # 菜单id
'permissions__group__menu__title', # 菜单标题
'permissions__url').distinct()
# 获取想要的数据,放入session,专门用于生成菜单
menu_list = []
for row in permission_list:
temp = {
'id':row['permissions__id'],
'title':row['permissions__title'],
'pid':row['permissions__parent_id'],
'url':row['permissions__url'],
'menu_id':row['permissions__group__menu_id'],
'menu_title':row['permissions__group__menu__title'],
}
menu_list.append(temp)
request.session[settings.MENU_SESSION_KEY] = menu_list
# 用于做权限的验证
permission_dict = {}
for item in permission_list:
group_id = item['permissions__group_id']
if group_id in permission_dict:
permission_dict[group_id]['urls'].append(item['permissions__url'])
permission_dict[group_id]['codes'].append(item['permissions__code'])
else:
permission_dict[group_id] = {
'urls': [item['permissions__url'], ],
'codes': [item['permissions__code'], ]
}
request.session[settings.PERMISSION_SESSION_KEY] = permission_dict
- 创建 粒度到按钮级别的权限功能,封装
class BasePermission(object):
def __init__(self,codes):
self.codes = codes
def list(self):
if 'list' in self.codes:
return True
def add(self):
if 'add' in self.codes:
return True
def edit(self):
if 'edit' in self.codes:
return True
def delete(self):
if 'del' in self.codes:
return True
- view 视图配置
from django.shortcuts import render,HttpResponse
from rbac import models
from rbac.service.init_permission import init_permission
from rbac.permission.base import BasePermission
import re
from django.conf import settings
class OrderPermission(BasePermission):
def report(self):
if 'report' in self.codes:
return True
def login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
user = request.POST.get('user')
pwd = request.POST.get('pwd')
user = models.UserInfo.objects.filter(name=user,password=pwd).first()
if user:
# 初始化权限信息
init_permission(user,request)
return HttpResponse('登录成功')
return render(request, 'login.html')
def users(request):
# 增删改查
per = BasePermission(request.permission_codes)
# 生成动态菜单
current_url = request.path_info
menu_list = request.session[settings.MENU_SESSION_KEY]
menu_dict = {}
for item in menu_list:
pid = item['pid']
if not pid:
item['active'] = False
menu_dict[pid] = item
for item in menu_list:
pid = item['pid']
url = "^%s$" % item['url']
if re.match(url, current_url):
if pid:
menu_dict[pid]['active'] = True
else:
item['active'] = True
return render(request,'users.html',{'per':per})
def orders(request):
# 增删改查+报表
per = OrderPermission(request.permission_codes)
return HttpResponse('订单列表')
def orders_add(request):
print(request.permission_codes)
return HttpResponse('添加订单')
# ###################### 测试:动态菜单 #######################
def menu1(request):
return render(request,'menu1.html')
def menu2(request):
return render(request,'menu2.html')
def menu3(request):
return render(request,'menu3.html')
def dynamic_menu(request):
menu_dict = {
1: {
'title': '菜单一',
'active': True,
'children': [
{'title': '权限一', 'url': '/xxxxx/', 'active': True},
{'title': '权限二', 'url': '/xxxxx/', 'active': False },
]
},
2: {
'title': '菜单二',
'active': False,
'children': [
{'title': '权限三', 'url': '/xxxxx/', 'active': False},
{'title': '权限四', 'url': '/xxxxx/', 'active': False},
]
}
}
return render(request,'dynamic_menu.html',{'menu_dict':menu_dict})