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
权限如何控制到行级别?
加条件表