RBAC 基于角色的权限控制
(Role-Based Access Control)
-
为什么要有权限:
区分用户的 功能
在web开发中 url代表权限
表结构:
from django.db import models
class Permission(models.Model):
url = models.CharField(max_length=32, verbose_name='权限')
title = models.CharField(max_length=32, verbose_name='标题')
class Meta:
verbose_name_plural = '权限'
verbose_name = '权限'
def __str__(self):
return self.title
class Role(models.Model):
"""
角色表
"""
name = models.CharField(max_length=32, verbose_name='名称')
permissions = models.ManyToManyField('Permission', verbose_name='角色拥有的权限', blank=True)
def __str__(self):
return self.name
class User(models.Model):
"""
用户表
"""
name = models.CharField(max_length=32, verbose_name='名称')
password = models.CharField(max_length=32, verbose_name='密码')
roles = models.ManyToManyField('Role', verbose_name='用户拥有的角色', blank=True)
def __str__(self):
return self.name
流程
录入权限信息到数据库
-
登录成功后保存用户的权限到session中
def login(request): if request.method == 'POST': user = request.POST.get('user') pwd = request.POST.get('pwd') obj = models.User.objects.filter(name=user, password=pwd).first() if not obj: return render(request, 'login.html', {'error_msg': '用户名或密码错误'}) # 登陆成果 保存权限的信息 ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 'permissions__title').distinct() # 保存权限信息 request.session[settings.PERMISSION_SESSION_KEY] = list(ret) return redirect(reverse('customer_list')) return render(request, 'login.html')
在setting 配置中 配置
# ################################ 权限的配置 ################################ # 权限的key PERMISSION_SESSION_KEY = 'permissions' # 菜单的key PERMISSION_MENU_KEY = 'menus' WHITE_LIST = [ r'^/login/$', r'^/reg/$', r'^/admin/.*', ]
-
中间件中对权限进行检验(白名单)
class RbacMiddleware(MiddlewareMixin): def process_request(self, request): # 1. 获取当前访问的URL url = request.path_info # 白名单 for i in settings.WHITE_LIST: if re.match(i, url): return # 2. 获取当前用户的权限信息 无权限的 跳转 login permission_list = request.session.get(settings.PERMISSION_SESSION_KEY) if not permission_list: return redirect(reverse('login')) # 3. 权限的校验 for i in permission_list: if re.match(r"^{}$".format(i['url']), url): return # 拒绝访问 return HttpResponse('没有访问权限')
一级菜单
动态生成一级菜单
-
表结构
- 权限表 角色表 用户表
- 权限和角色的多对多关系表 用户和角色的多对多关系表
from django.db import models class Permission(models.Model): """ 权限表 可做菜单的权限 is_menu=True 不可做菜单的权限 is_menu=False """ url = models.CharField(max_length=32, verbose_name='权限') title = models.CharField(max_length=32, verbose_name='标题') is_menu = models.BooleanField(default=False, verbose_name='是否是菜单') icon = models.CharField(max_length=64, null=True, blank=True, verbose_name='图标') class Meta: verbose_name_plural = '权限' verbose_name = '权限' def __str__(self): return self.title class Role(models.Model): """ 角色表 """ name = models.CharField(max_length=32, verbose_name='名称') permissions = models.ManyToManyField('Permission', verbose_name='角色拥有的权限', blank=True) def __str__(self): return self.name class User(models.Model): """ 用户表 """ name = models.CharField(max_length=32, verbose_name='名称') password = models.CharField(max_length=32, verbose_name='密码') roles = models.ManyToManyField('Role', verbose_name='用户拥有的角色', blank=True) def __str__(self): return self.name
-
流程:
- 保存菜单的信息到 session 中
# 在 rbac app中 定义 一个文件 处理 信息的 录入 def init_permisson(request, obj): """ 权限信息的初识化 保存权限和菜单的信息 :param request: :param obj: :return: """ ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url', 'permissions__title', 'permissions__is_menu', 'permissions__icon', ).distinct() # 存放权限信息 permission_list = [] # 存放菜单信息 menu_list = [] for item in ret: # 将所有的权限信息添加到permission_list permission_list.append({'url': item['permissions__url']}) # 把是菜单的权限信息放入到menu_list if item.get('permissions__is_menu'): menu_list.append({'url': item['permissions__url'], 'title': item['permissions__title'], 'icon': item['permissions__icon']}) # 保存权限信息 request.session[settings.PERMISSION_SESSION_KEY] = permission_list # 保存菜单信息 request.session[settings.PERMISSION_MENU_KEY] = menu_list
-
定义 菜单的 heml 代码块 使用 inclusion_tag
html 代码 menu.html
<div class="static-menu"> {# <a href="/customer/list/" class="active">#} {# <span class="icon-wrap"><i class="fa fa-connectdevelop"></i></span> 客户管理</a>#} {# <a href="/payment/list/">#} {# <span class="icon-wrap"><i class="fa fa-code-fork"></i></span> 账单管理</a>#} {% for item in menu_list %} <a href="{{ item.url }}" class="{{ item.class }}"> <span class="icon-wrap"><i class="fa {{ item.icon }}"></i></span> {{ item.title }}</a> {% endfor %} </div>
inclusion_tag
from django import template from django.conf import settings import re register = template.Library() @register.inclusion_tag('rbac/menu.html') def menu(request): menu_list = request.session.get(settings.PERMISSION_MENU_KEY) for item in menu_list: if re.match("^{}$".format(item['url']), request.path_info): item['class'] = 'active' break return {'menu_list': menu_list}
-
定义 代码块的css 样式 menu.css
.static-menu .icon-wrap { width: 20px; display: inline-block; text-align: center; } .static-menu a { text-decoration: none; padding: 8px 15px; border-bottom: 1px solid #ccc; color: #333; display: block; background: #efefef; background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa)); background: -ms-linear-gradient(bottom, #efefef, #fafafa); background: -moz-linear-gradient(center bottom, #efefef 0%, #fafafa 100%); background: -o-linear-gradient(bottom, #efefef, #fafafa); filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff'); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')"; box-shadow: inset 0px 1px 1px white; } .static-menu a:hover { color: #2F72AB; border-left: 2px solid #2F72AB; } .static-menu a.active { color: #2F72AB; border-left: 2px solid #2F72AB; }
-
在 母版中 导入 css 样式 以及使用 inclusion_tag 返回菜单列表
<link rel="stylesheet" href="{% static 'css/menu.css' %} "/> {% load rbac %} {% menu request %}
二级菜单
动态生成二级菜单
表结构:
from django.db import models
# Create your models here.
class Menu(models.Model):
''' 一级菜单 '''
title = models.CharField(max_length=32)
icon = models.CharField(max_length=64, null=True, blank=True, verbose_name='图标')
def __str__(self):
return self.title
class Permission(models.Model):
'''
权限表
可以做二级菜单的权限 menu 关联 菜单表
不可以做菜单的权限 menu=null
'''
url = models.CharField(max_length=32, verbose_name='路径')
title = models.CharField(max_length=32, verbose_name='描述')
menu = models.ForeignKey('Menu', null=True, blank=True)
def __str__(self):
return self.title
class Role(models.Model):
'''
角色表 部门
'''
name = models.CharField(max_length=32, verbose_name='角色')
permissions = models.ManyToManyField('Permission', verbose_name='角色拥有的权限', blank=True)
def __str__(self):
return self.name
class User(models.Model):
'''
用户表
'''
name = models.CharField(max_length=32, verbose_name='用户名')
password = models.CharField(max_length=32, verbose_name='密码')
roles = models.ManyToManyField('Role', blank=True, verbose_name='用户拥有的角色')
def __str__(self):
return self.name
class Meta:
verbose_name_plural = '用户'
verbose_name = '用户表'
二级菜单的 数据结构
data = [{
'permissions__url': '/customer/list/',
'permissions__title': '客户列表',
'permissions__menu__title': '信息列表',
'permissions__menu__icon': 'fa-code-fork',
'permissions__menu_id': 1
},
{
'permissions__url': '/customer/list/',
'permissions__title': '用户列表',
'permissions__menu__title': '信息列表',
'permissions__menu__icon': 'fa-code-fork',
'permissions__menu_id': 1
},{
'permissions__url': '/customer/add/',
'permissions__title': '增加客户',
'permissions__menu__title': None,
'permissions__menu__icon': None,
'permissions__menu_id': None
}, {
'permissions__url': '/customer/edit/(\\d+)/',
'permissions__title': '编辑客户',
'permissions__menu__title': None,
'permissions__menu__icon': None,
'permissions__menu_id': None
}]
"""
{
1:{
'title':'信息列表',
'icnon':'fa-code-fork',
'children': [
{'title': '客户列表','url':'/customer/list/ },
{'title': '用户列表','url':'/customer/list/ }
]
}
}
"""
流程:
- 保存菜单的信息到 session 中
# 在 rbac app中 定义 一个文件 处理 信息的 录入
def init_permisson(request, obj):
"""
权限信息的初识化
保存权限和菜单的信息
:param request:
:param obj:
:return:
"""
ret = obj.roles.all().filter(permissions__url__isnull=False).values('permissions__url',
'permissions__title', 'permissions__menu__title',
'permissions__menu__icon',
'permissions__menu_id',
).distinct()
print(ret)
# 存放权限信息
permission_list = []
# 存放菜单信息
menu_dict = {} VGB M,K.L/;['}.+
*6654=-908877 }0']
for item in ret:
# 将所有的权限信息添加到permission_list
permission_list.append({'url': item['permissions__url']})
# 构造菜单的数据结构
menu_id = item.get('permissions__menu_id')
# 表示当前的权限是不做菜单的权限
if not menu_id:
continue
# 可以做菜单的权限
if menu_id not in menu_dict:
menu_dict[menu_id] = {
'title': item['permissions__menu__title'], # 一级菜单标题
'icon': item['permissions__menu__icon'],
'children': [{'title': item['permissions__title'], 'url': item['permissions__url']}]
}
else:
menu_dict[menu_id]['children'].append(
{'title': item['permissions__title'], 'url': item['permissions__url']})
print(menu_dict)
# 保存权限信息
request.session[settings.PERMISSION_SESSION_KEY] = permission_list
# 保存菜单信息
request.session[settings.PERMISSION_MENU_KEY] = menu_dict
-
定义html代码块 及 inclusion_tag
html代码块
<div class="multi-menu">
{% for item in menu_list %}
<div class="item">
<div class="title"><i class="fa {{ item.icon }}"></i> {{ item.title }} </div>
<div class="body">
{% for child in item.children %}
<a href="{{ child.url }}">{{ child.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
inclusion_tag rbac.py
from django import template
from django.conf import settings
import re
register = template.Library()
@register.inclusion_tag('rbac/menu.html')
def menu(request):
menu_dict = request.session.get(settings.PERMISSION_MENU_KEY)
return {'menu_list': menu_dict.values()}
- Css样式 css/menu.css
.multi-menu .item {
background-color: white;
}
.multi-menu .item > .title {
padding: 10px 5px;
border-bottom: 1px solid #dddddd;
cursor: pointer;
color: #333;
display: block;
background: #efefef;
background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #efefef), color-stop(1, #fafafa));
background: -ms-linear-gradient(bottom, #efefef, #fafafa);
background: -o-linear-gradient(bottom, #efefef, #fafafa);
filter: progid:dximagetransform.microsoft.gradient(startColorStr='#e3e3e3', EndColorStr='#ffffff');
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fafafa',EndColorStr='#efefef')";
box-shadow: inset 0 1px 1px white;
}
.multi-menu .item > .body {
border-bottom: 1px solid #dddddd;
}
.multi-menu .item > .body a {
display: block;
padding: 5px 20px;
text-decoration: none;
border-left: 2px solid transparent;
font-size: 13px;
}
.multi-menu .item > .body a:hover {
border-left: 2px solid #2F72AB;
}
.multi-menu .item > .body a.active {
border-left: 2px solid #2F72AB;
}
使用:
在 母版中 导入 css 样式 以及使用 inclusion_tag 返回菜单列表
<link rel="stylesheet" href="{% static 'css/menu.css' %} "/>
{% load rbac %}
{% menu request %}
二级菜单的 点击动画
$(function () {
$('.item .title').click(function () {
{#$(this).next().toggleClass('hide')#}
$('.item .title').siblings($(this)).slideUp(100);
$(this).next().slideDown(100);
{#展开当前 一级菜单 下的 二级菜单#}
{#$(this).next().removeClass('hide');#}
{# 关闭其他菜单 下的 二级菜单#}
{#$(this).parent().siblings().find('.body').addClass('hide');#}
})
})
菜单的排序
给一级菜单进行排序
在model 中 添加 权重 字段
# 1.model 中 的权限表 添加 权重字段
weight = models.IntegerField(default=1, verbose_name='权重')
# 2.查询出权重 将其加入到 menu_dict 的一级菜单中
# 3. 在 inclusion_tag 中 给菜单进行排序
# 导入 OrderedDict 有序字典 因为字典是无序的 查询出的顺序不一定
from collections import OrderedDict
ordered_dict = OrderedDict()
# 给菜单进行排序
for i in sorted(menu_dict, key=lambda a: menu_dict[a]['weight'], reverse=True):
# 将 menu_dict 循环添加到 实例化的 OrderedDict() 中
ordered_dict[i] = menu_dict[i]
非菜单权限的归属问题
-
信息列表 # 一级菜单
客户列表 # 二级菜单
添加客户 # 非菜单权限 编辑客户 # 非菜单权限
-
财务列表
缴费列表
-
权限表中
menu_id : 关联的菜单
parent_id: 外键关联自己 三级菜单 ‘self’ 判断从属于那个权限
id url title menu_id parent_id
1 /list/ 客户列表 1 null
2 /add/ 添加客户 null 1
-
给权限表中 加入 paren 外键的字段 可以为空
paren = models.ForeignKey('self', null=True, blank=True)
-
获取 paren 关联的 id 也就是 父id 从属于那个权限 以及 本权限的id
permission = obj.roles.all().filter(permissions__url__isnull=False) .values('permissions__url', 'permissions__title', 'permissions__menu__title', 'permissions__menu__font', 'permissions__menu__weight', # 权重 'permissions__menu_id', #'permissions__weight', 'permissions__paren_id', # 关联的 归属id 'permissions__id', # 本权限的 id ).distinct()
-
將 id pid 存放到 permissions_list 中的每个字典中
permissions_list.append({'url': item['permissions__url'], 'id': item['permissions__id'], 'pid': item['permissions__paren_id'], }) #paren 括弧
-
存放 权限id 到 二级菜单的 children中
'children': [{'title': item['permissions__title'], 'url': item['permissions__url'], 'id': item['permissions__id'], }]}
-
从 中间件中获取到 两个id 做判断 將 从属的 id 存放到 request 对象中
# 记录 paren 的 id 到 request 中 id, pid = i['id'], i['pid'] if pid: # 有PID表示当前访问的权限是子权限 它有父权限 要让这个父权限展开 request.xxx = pid else: # 表示当前访问的权限是父权限 要让自己展开 request.xxx = id
-
从 inclusion_tag 中 取到 没个菜单的 children 循环 判断 从属的 id 是不是 本 权限的 id
给从属的 权限加上 显示 及 active item['class'] = 'hide' for child in item['children']: # if re.match("^{}$".format(child['url']), request.path_info): if request.xxx == child['id']: child['class'] = 'active' item['class'] = ''
路径导航
面包屑 导航
注意 :
权限信息放入到session中。进行json序列化
字段的key 如果是数字化,会变成数字字符串
-
將 权限 dict 中加入 title 描述
字典中 本身就有 路径 以及 描述
permissions_dict[item['permissions__id']] = ({'url': item['permissions__url'], 'id': item['permissions__id'], 'pid': item['permissions__paren_id'], 'title': item['permissions__title']})
-
在 中间件中 创建 一个 面包屑的列表 存到 request 中
# 创建列表 使用 反射 添加到 request 中 名字在 settings 中配置 setattr(request, setting.BREADCRUMB, [ {'url': reverse('index'), 'title': '首页'},]) # 其他导航路径的添加 if pid: # 有PID表示当前访问的权限是子权限 它有父权限 要让这个父权限展开 request.xxx = pid # 获取 父 权限的 内容 p_dict = permissions_dict[str(pid)] # 將父权限的内容 添加到 面包屑列表中 getattr(request,setting.BREADCRUMB).append( {'url': p_dict['url'], 'title': p_dict['title']} ) # 添加子 内容 到 列表中 getattr(request,setting.BREADCRUMB).append( {'url': i['url'], 'title': i['title']} ) else: # 表示当前访问的权限是父权限 要让自己展开 request.xxx = id getattr(request,setting.BREADCRUMB).append( {'url': i['url'], 'title': i['title']} )
-
信创建 inclusion_tag 获取 面包屑列表
@register.inclusion_tag('rbac/breadcrumb.html') def breadcrumb(request): breadcrumb_list = getattr(request,setting.BREADCRUMB) return {'breadcrumb_list': breadcrumb_list}
HTML 页面 rbac/breadcrumb.html
<ol class="breadcrumb no-radius no-margin" style="border-bottom: 1px solid #ddd;"> {% for bread in breadcrumb_list %} {% if forloop.last %} <li>{{ bread.title }}</li> {% else %} <li><a href="{{ bread.url }}">{{ bread.title }}</a></li> {% endif %} {% endfor %} </ol>
-
在 母版中使用
{% breadcrumb request %}
用到反射 (反射复习)
权限控制到按钮级别
同过 name 别名 来判断 路径 將name 存放在 权限中
name = models.CharField(max_length=32, verbose_name='url别名')
存放所有 权限的 别名
-
获取 自己的url别名 以及
'permissions__paren__name', # 关联的 归属 权限 名字 'permissions__name', # url别名
-
用别名做 key 添加 关联字段的 名字
# 添加所有的 权限 url 使用 url 别名 当做 Key permissions_dict[item['permissions__name']] = ( {'url': item['permissions__url'], 'id': item['permissions__id'], 'p_name': item['permissions__paren__name'], # 添加 查询 当前关联的 权限的名字 'pid': item['permissions__paren_id'], 'title': item['permissions__title']})
在中间件中 获取 p_name 的值
通过 值 找到 父及菜单的所有内容
id, pid, p_name = i['id'], i['pid'], i['p_name'] p_dict = permissions_dict[p_name] 查找 父及菜单内容
-
创建 filter 过滤器 反回 T or F 来判断 是否显示 标签
@register.filter() def has_permission(request, name): # 判断name是否在权限的字典中 if name in request.session.get(settings.PERMISSION_SESSION_KEY): return True
-
在母版中 使用
{% if request|has_permission:'customer_edit' and request|has_permission:'customer_del' %} {% if request|has_permission:'customer_del' %}
区分 一二级联动 菜单
获取所有的 一级 菜单 一级 权限 信息 做展示
修改 权限 信息的 数据结构 区分 二三级 菜单 循环进行展示
# 显示 一级菜单 联动 二及菜单
def menu_list(request):
menu_obj = models.Menu.objects.all()
# 获取 菜单的 id
mid = request.GET.get('mid')
if mid:
# 如果有 条件 则对数据进行筛选
permission_all = models.Permission.objects.filter(Q(menu_id=mid) | Q(paren__menu_id=mid))
else:
permission_all = models.Permission.objects.all()
# 实例化 有序字典 修改 数据结构 將 数据分为 二三级菜单 存入 有序字典中
permission_dict = OrderedDict()
for item in permission_query_dict:
# 判断 二级菜单
if item.get('menu_id'):
permission_dict[item['id']] = item
item['children'] = []
for item in permission_query_dict:
# 判断三级菜单
pid = item.get('paren_id')
if pid:
permission_dict[pid]['children'].append(item)
print(permission_dict)
html
{% extends 'layout.html' %}
{% block css %}
<style>
.permission-area tr.root {
background-color: #dff0d8;
}
.menu-body tr.active {
background-color: #f1f7fd;
border-left: 3px solid #fdc00f;
}
</style>
{% endblock %}
{% block content %}
<div class="col-sm-3">
<div class="panel panel-info" style="margin: 20px;">
<div class="panel-heading"><i class="fa fa-book"></i> 菜单管理
<a href="{% url 'menu_add' %}" class="btn btn-sm btn-success pull-right"
style="margin-top: -2px; padding: 2px 7px">
<i class="fa fa-plus"></i> 新建</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>名称</th>
<th>图标</th>
<th>操作</th>
</tr>
</thead>
<tbody class="menu-body">
{% for field in menu_obj %}
{# field.pk|safe 将数字转义为字符串 #}
<tr class="{% if field.pk|safe == mid %}active{% endif %}">
<td><a href="?mid={{ field.pk }}">{{ field.title }}</a></td>
<td><i class="fa {{ field.font }}"></i></td>
<td>
<a href="{% url 'menu_edit' field.pk %}" style=""><i class="fa fa-pencil-square-o"></i></a>
<a href="{% url 'menu_del' field.pk %}" style="color: red;"><i class="fa fa-trash"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="col-sm-9">
<div class="panel panel-info" style="margin: 20px;">
<div class="panel-heading"><i class="fa fa-th"></i> 权限管理
<a href="" class="btn btn-sm btn-warning pull-right" style="margin-top: -2px; padding: 2px 7px">
<i class="fa fa-mail-forward"></i> 批量操作</a>
<a href="{% url 'permission_add' %}" class="btn btn-sm btn-success pull-right" style="margin-top: -2px; padding: 2px 7px">
<i class="fa fa-plus-circle"></i> 新建</a>
</div>
<table class="table table-hover">
<thead>
<tr>
<th>描述</th>
<th>URR</th>
<th>URR别名</th>
<th>操作</th>
</tr>
</thead>
<tbody class="permission-area">
{% for p_permission in permission_dict %}
{# 二级菜单的 展示 #}
<tr class="root" id="{{ p_permission.id }}">
<td class="title">
<i class="fa fa-caret-down"></i>
{{ p_permission.title }}</td>
<td>{{ p_permission.url }}</td>
<td>{{ p_permission.name }}</td>
<td>
<a href="{% url 'permission_edit' p_permission.id %}" style=""><i class="fa fa-pencil-square-o"></i></a>
<a href="" style="color: red;"><i class="fa fa-trash"></i></a>
</td>
</tr>
{% for c_permission in p_permission.children %}
三级菜单的展示
<tr pid="{{ p_permission.id }}">
<td>{{ c_permission.title }}</td>
<td>{{ c_permission.url }}</td>
<td>{{ c_permission.name }}</td>
<td>
<a href="{% url 'permission_edit' c_permission.id %}" style=""><i class="fa fa-pencil-square-o"></i></a>
<a href="" style="color: red;"><i class="fa fa-trash"></i></a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
{% block js %}
<script>
$('.permission-area').on('click', '.root .title', function () {
var caret = $(this).find('i');
var id = $(this).parent().attr('id');
if (caret.hasClass('fa-caret-right')) {
caret.removeClass('fa-caret-right').addClass('fa-caret-down');
$(this).parent().nextAll('tr[pid="' + id + '"]').removeClass('hide');
} else {
caret.removeClass('fa-caret-down').addClass('fa-caret-right');
$(this).parent().nextAll('tr[pid="' + id + '"]').addClass('hide');
}
})
</script>
{% endblock %}
应用rbac权限组件
有一个要应用的项目
把rbac的app拷贝到新项目中,注册
-
生成rbac相关的表
- 删除rbac/migrations下的除init之外的py文件
- 执行数据库迁移的命令
-
使用admin录入权限信息
- 创建超级用户
- 登录admin录入权限信息
- 权限信息 url地址 不带^$
- 角色 给角色分配权限
- 用户 给用户分配角色
-
应用登录后权限信息的初始化
from rbac.service.permission import init_permisson from rbac.models import User # 登录成功后 init_permisson(request,obj)
使用权限的中间件
MIDDLEWARE = [
...
'rbac.middlewares.rbac.RbacMiddleware'
]
在settings中进行配置
# ################################ 权限的配置 ################################
# 权限的key
PERMISSION_SESSION_KEY = 'permissions'
# 菜单的key
PERMISSION_MENU_KEY = 'menus'
WHITE_LIST = [
r'^/login/$',
r'^/reg/$',
r'^/admin/.*',
]
7.应用动态生成一级菜单
- 使用menu 的inclusion_tag
- 应用css样式
<link rel="stylesheet" href="{% static 'css/menu.css' %} "/>
{% load rbac %}
{% menu request %}
2.动态生成二级菜单
信息列表 - 一级菜单
客户列表 - 二级菜单
财务列表
缴费列表
icon 图标库 爬虫
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from django.utils.safestring import mark_safe
import requests
from bs4 import BeautifulSoup
response = requests.get(
url='http://fontawesome.dashgame.com/',
)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'html.parser')
web = soup.find(attrs={'id': 'web-application'})
icon_list = []
for item in web.find_all(attrs={'class': 'fa-hover'}):
tag = item.find('i')
class_name = tag.get('class')[1]
icon_list.append([class_name, str(tag)])
print(icon_list)
应用权限组件
- 拷贝rbac组件到新的项目中,注册app
- 修改用户表,继承rbac中的User
class User(models.Model):
"""
用户表
"""
# name = models.CharField(max_length=32, verbose_name='名称')
# password = models.CharField(max_length=32, verbose_name='密码')
roles = models.ManyToManyField(Role, verbose_name='用户拥有的角色', blank=True)
# def __str__(self):
# return self.name
class Meta:
abstract = True # 数据库迁移时候不会生成表,用来做基类
class UserProfile(User, models.Model):
- 执行数据库迁移的命令
- 删除rbac下migrations中的记录
- 注释掉admin中User表
- roles = models.ManyToManyField(Role, verbose_name='用户拥有的角色', blank=True) # 关联的字段不要写成字符串形式
- 设置rbac的url
url(r'rbac/', include('rbac.urls',namespace='rbac'))
菜单管理
-
权限的录入
- 所有的url要头name
- 不要忽略rbac namespace
- 注意url和别名的长度
- 构建层级结构
角色管理
-
分配权限
- 注意用新的用户表替换rbac中的User
- 给不同角色分配权限
- 给不同用户分配角色
-
应用上权限
- 应用中间件 在settings中写上权限的配置
# 权限的key PERMISSION_SESSION_KEY = 'permissions' # 菜单的key PERMISSION_MENU_KEY = 'menus' WHITE_LIST = [ r'^/login/$', r'^/reg/$', r'^/admin/.*', ] NO_PERMISSION_LIST = [ r'^/index/$', r'^/logout/$', ] # 路径导航 BREADCRUMB = 'breadcrumb_list' # 路径导航 CURRENT_MENU = 'current_parent_id'
- 登录成功后权限信息的初识化
from rbac.service.permission import init_permisson # 权限信息的初始化 init_permisson(request,obj)
-
动态生成二级菜单
- 在layout中使用
```
导入CSS js
{% load rbac %}
{% menu request %}
```
-
应用路径导航
{% breadcrumb request %}
权限控制到按钮级别
{% load rbac %}
{% if request|has_permission:"consult_add" %}
<a href="{% url 'consult_add' %}" class="btn btn-primary btn-sm">添加</a>
{% endif %}
权限如何能不重新登录,就应用新的权限?
用户表加字段 存 session_key
-
通过session_key 拿到用户的session数据 更新权限
from django.contrib.sessions.models import Session from django.contrib.sessions.backends.db import SessionStore decode encode
权限如何控制到行级别?
加条件表