rbac——界面、权限
一、模板继承
知识点:
users.html / roles.html 继承自 base.html
页面滚动时,固定
.menu {
background-color: bisque;
position: fixed;
top: 60px;
bottom: 0px;
left: 0px;
width: 200px;
}
.content {
position: fixed;
top: 60px;
bottom: 0;
right: 0;
left: 200px;
overflow: auto; /* 滚动条 */
}
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 引入 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<style>
.header {
width: 100%;
height: 60px;
background-color: #336699;
}
.menu {
background-color: bisque;
position: fixed;
top: 60px;
bottom: 0px;
left: 0px;
width: 200px;
}
.content {
position: fixed;
top: 60px;
bottom: 0;
right: 0;
left: 200px;
overflow: auto; /* 滚动条 */
}
</style>
</head>
<body> <div class="header">
{{ user.name }}
</div>
<div class="contain">
<div class="menu">
menu
</div>
<div class="content">
{% block con%} {% endblock %}
</div>
</div> </body>
</html>
users.html:
{% extends 'base.html' %} {% block con %}
<h4>用户列表</h4>
{% for user in user_list %}
<p>{{ user }}</p>
{% endfor %} {% endblock con%}
roles.html:
{% extends 'base.html' %} {% block con %}
<h4>角色列表</h4>
<ul>
{% for role in role_list %}
<p>{{ role }}</p>
{% endfor %}
</ul>
{% endblock %}
二、在users.html中添加table
{% extends 'base.html' %} {% block con %}
<h4>用户列表</h4>
<a href="/users/add" class="btn btn-primary">添加用户</a>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>角色</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in user_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ user.name }}</td>
<td>
{% for role in user.roles.all %}
{{ role.title }}
{% endfor %} </td>
<td>
<a href="" class="btn btn-danger">删除</a>
<a href="" class="btn btn-warning">编辑</a>
</td>
</tr>
{% endfor %} </tbody>
</table>
{% endblock %}
注意:
(1)有一些用户有多重角色,需要将这些角色拿到显示在表格中的方法
<td>
{% for role in user.roles.all %}
{{ role.title }}
{% endfor %}
</td>
(2)django模板中的forloop模板变量:在每个`` {% for %}``循环里有一个称为`` forloop`` 的模板变量。这个变量有一些提示循环进度信息的属性。
forloop.counter 总是一个表示当前循环的执行次数的整数计数器。 这个计数器是从1开始的,所以在第一次循环时 forloop.counter 将会被设置为1。
三、根据权限决定是否显示按钮
在页面往往有一些功能按钮,如果该用户没有权限,就不将这个按钮开放给当前用户,这样处理优于向用户提示“没有使用权限”。
1、在模板中权限按钮控制的简单形式
{# 根据是否有权限显示添加用户按钮 #}
{% if "/users/add" in permission_list %}
<a href="/users/add" class="btn btn-primary">添加用户</a>
{% endif %}
处理带有正则表达式的url:
<td>
{% if '/users/delete/(d+)' in permission_list %}
<a href="/users/delete/{{ user.pk }}" class="btn btn-danger">删除</a>
{% endif %}
<a href="" class="btn btn-warning">编辑</a>
</td>
这种方法是针对表做操作,根据表名去做判断。
如果希望if判断时url里面不带有表名字。roles和users合并用一个视图函数来处理。
2、admin修改显示,页面显示更多内容
rbac/admin.py:
from django.contrib import admin
# Register your models here.
from .models import * class PerConfig(admin.ModelAdmin):
list_display = ["title", "url"] admin.site.register(User)
admin.site.register(Role)
admin.site.register(Permission, PerConfig)
注意:list_display = [] 。
显示效果:
3、修改数据结构
添加一个权限组表。将每张表的增删改查,划到一个组里面!无论多复杂的,最终一定是对数据库的(增删改查)。
修改表结构,重新处理中间件,登录页面的目的:全是为了按钮的粒度,同一个模板,同一个视图,
显示不同的数据,权限。
(1)models.py代码
from django.db import models
# Create your models here. class User(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
roles = models.ManyToManyField(to="Role") def __str__(self):
return self.name class Role(models.Model):
title = models.CharField(max_length=32)
permissions = models.ManyToManyField(to="Permission") def __str__(self):
return self.title class Permission(models.Model):
title = models.CharField(max_length=32)
url = models.CharField(max_length=32)
# 操作
action = models.CharField(max_length=32, default="") # 默认值为空
# 分组
group = models.ForeignKey("PermissionGroup", default=1, on_delete=True) def __str__(self):
return self.title class PermissionGroup(models.Model):
title = models.CharField(max_length=32) def __str__(self):
return self.title
修改完后,在一次执行数据库迁移。
(2)再一次修改rbac/admin.py:
from django.contrib import admin
# Register your models here.
from .models import * class PerConfig(admin.ModelAdmin):
list_display = ["title", "url", "group", "action"] admin.site.register(User)
admin.site.register(Role)
admin.site.register(Permission, PerConfig)
admin.site.register(PermissionGroup)
(3)为权限添加action:
全部修改后:
修改之后,GROUP描述是对哪张表进行操作,ACTION是描述对这个表做什么操作。
(4)修改rbac_permission表的group_id信息,将角色操作类别的group_id修改为2
4、重写initial_session(user, request)函数
# -*- coding:utf-8 -*-
__author__ = 'Qiushi Huang' def initial_session(user,request):
"""
查看当前用户所有的权限
:param user:
:param request:
:return:
"""
# 方案1:
# permissions = user.roles.all().values("permissions__url").distinct()
# print(permissions) # <QuerySet [{'permissions__url': '/users/'}, {'permissions__url': '/users/add'}]>
#
# permission_list = []
# for item in permissions:
# permission_list.append(item["permissions__url"])
#
# print(permission_list)
#
# request.session["permission_list"] = permission_list # 方案2:
# 角色表跨到权限表查找
permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct()
print("permissions", permissions) # 有一个权限QuerySet中就有一个字典
"""
permissions <QuerySet [{'permissions__url': '/users/',
'permissions__group_id': 1,
'permissions__action': 'list'}]>
"""
# 对上述数据进行处理: 以组为键,以字典为值
permission_dict = {}
for item in permissions:
gid = item.get("permissions__group_id") if not gid in permission_dict:
permission_dict[gid] = {
"urls": [item["permissions__url"], ],
"actions": [item["permissions__action"], ] }
else:
# 组id已经在字典中
permission_dict[gid]["urls"].append(item["permissions__url"])
permission_dict[gid]["actions"].append(item["permissions__action"]) print(permission_dict) # {1: {'urls': ['/users/', '/users/add', '/users/delete/(\\d+)', '/users/edit/(\\d+)'],
# 'actions': ['list', 'add', 'delete', 'edit']}} request.session['permission_dict']=permission_dict
注意:
(1)在session中注册权限字典
前面是在session中注册权限列表:
request.session['permission_list'] = permission_list
现在需要在session中注册的是权限字典:
request.session['permission_dict'] = permission_dict
(2)从角色表到权限表跨表查询权限路径、权限组ID、权限action
# 角色表跨到权限表查找
permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct()
print("permissions", permissions) # 有一个权限QuerySet中就有一个字典
"""
permissions <QuerySet [{'permissions__url': '/users/',
'permissions__group_id': 1,
'permissions__action': 'list'}]>
"""
(3)对上述数据进行处理:以组为键、以字典为值
{
1: {
"url": ['/users/',],
"actions": ['list',]
},
}
如果用户操作多个权限:
{
1: {
'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'],
'actions': ['list', 'add', 'delete', 'edit']
},
}
如果除了有用户操作权限还有角色操作权限:
{
1: {
'urls': ['/users/', '/users/add/', '/users/delete/(\\d+)/', '/users/edit/(\\d+)/'],
'actions': ['list', 'add', 'delete', 'edit']
},
2: {
'urls': ['/roles/'],
'actions': ['list']
}
}
5、改写中间件rbac.py中的VaildPermission类
# -*- coding:utf-8 -*-
__author__ = 'Qiushi Huang' import re
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirect class ValidPermission(MiddlewareMixin): def process_request(self, request): # 当前访问路径
current_path = request.path_info # 当前路径的属性 ########### 检查是否属于白名单 #############
valid_url_list = ['/login/', '/reg/', '/admin/.*']
for valid_url in valid_url_list:
ret = re.match(valid_url, current_path)
if ret:
return # 等同于return none ############### 检验是否登录 ##############
user_id = request.session.get("user_id") if not user_id:
return redirect("/login/") ################ 校验权限1 #################
# permission_list = request.session.get("permission_list")
#
# flag = False
# for permission in permission_list:
# permission = "^%s$" % permission
# ret = re.match(permission, current_path) # 第一个参数是匹配规则,第二个参数是匹配项
# if ret:
# flag = True
# break
# if not flag:
# # 如果没有访问权限
# return HttpResponse("没有访问权限!") ################ 校验权限2 #################
permission_dict = request.session.get('permission_dict') for item in permission_dict.values(): # 循环只取字典的值
urls = item["urls"]
for reg in urls:
reg = "^%s$" % reg
ret = re.match(reg, current_path)
if ret:
print("actions", item["actions"])
request.actions = item["actions"]
return None return HttpResponse("没有访问权限!")
注意:
(1)中间件的request对象,给对象添加属性actions,未来视图中就可以通过request.actions拿到当前用户对这个表的所有操作权限。
request.actions = item["actions"]
(2)数据类型从数组变为了字典,数据处理方式略有不同。
6、改写users视图,视图添加Per类
class Per(object):
def __init__(self, actions):
self.actions = actions def add(self):
return "add" in self.actions def delete(self):
return "delete" in self.actions def edit(self):
return "edit" in self.actions def list(self):
return "list" in self.actions def users(request):
user_list = User.objects.all()
permission_list = request.session.get("permission_list")
print(permission_list) # ['/users/', '/users/add', '/roles/', '/users/delete/(\\d+)', '/users/edit/(\\d+)'] # 查询当前登录人的名字
id = request.session.get("user_id")
user = User.objects.filter(id=id).first() per = Per(request.actions) return render(request, "users.html", locals())
注意:
通过Per(request.actions)得到per对象,传到模板中可以通过per.edit\per.list等方式来判断是否拥有权限。增加阅读性。
7、users.html改写
{% extends 'base.html' %} {% block con %}
<h4>用户列表</h4>
{# 根据是否有权限显示添加用户按钮 #}
{% if per.add %}
<a href="/users/add" class="btn btn-primary">添加用户</a>
{% endif %}
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>序号</th>
<th>姓名</th>
<th>角色</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for user in user_list %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ user.name }}</td>
<td>
{% for role in user.roles.all %}
{{ role.title }}
{% endfor %}
</td>
<td>
{% if per.delete %}
<a href="/users/delete/{{ user.pk }}" class="btn btn-danger">删除</a>
{% endif %}
{% if per.edit %}
<a href="" class="btn btn-warning">编辑</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
显示效果:
8、总结
1、权限粒度控制
简单控制:
{% if "users/add" in permissions_list%}
2、更改数据库结构
class Permission(models.Model):
title = models.CharField(max_length=32)
url = models.CharField(max_length=32)
# 操作
action = models.CharField(max_length=32, default="") # 默认值为空
# 分组
group = models.ForeignKey("PermissionGroup", default=1, on_delete=True)
def __str__(self):
return self.title class PermissionGroup(models.Model):
title = models.CharField(max_length=32)
def __str__(self):
return self.title
3、登录验证
permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct()
4、构建permission_dict
5、中间件校验权限
permission_dict = request.session.get('permission_dict') for item in permission_dict.values(): # 循环只取字典的值
urls = item["urls"]
for reg in urls:
reg = "^%s$" % reg
ret = re.match(reg, current_path)
if ret:
print("actions", item["actions"])
request.actions = item["actions"]
return None return HttpResponse("没有访问权限!")
四、权限菜单显示
1、用户登录在initial_session中注册菜单权限并注册到session中
def initial_session(user, request):
"""
查看当前用户所有的权限
:param user:
:param request:
:return:
"""
# 方案1:
# permissions = user.roles.all().values("permissions__url").distinct()
# print(permissions) # <QuerySet [{'permissions__url': '/users/'}, {'permissions__url': '/users/add'}]>
#
# permission_list = []
# for item in permissions:
# permission_list.append(item["permissions__url"])
#
# print(permission_list)
#
# request.session["permission_list"] = permission_list # 方案2:
# 角色表跨到权限表查找
permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action").distinct()
print("permissions", permissions) # 有一个权限QuerySet中就有一个字典
"""
permissions <QuerySet [{'permissions__url': '/users/',
'permissions__group_id': 1,
'permissions__action': 'list'}]>
"""
# 对上述数据进行处理: 以组为键,以字典为值
permission_dict = {}
for item in permissions:
gid = item.get("permissions__group_id") if not gid in permission_dict:
permission_dict[gid] = {
"urls": [item["permissions__url"], ],
"actions": [item["permissions__action"], ] }
else:
# 组id已经在字典中
permission_dict[gid]["urls"].append(item["permissions__url"])
permission_dict[gid]["actions"].append(item["permissions__action"]) print(permission_dict) # {1: {'urls': ['/users/', '/users/add', '/users/delete/(\\d+)', '/users/edit/(\\d+)'],
# 'actions': ['list', 'add', 'delete', 'edit']}} request.session['permission_dict'] = permission_dict # 注册菜单权限
permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action",
"permissions__group__title").distinct()
print("permissions", permissions) menu_permission_list = [] # 菜单栏中权限列表:空列表
for item in permissions:
# item是里面的字典
if item["permissions__action"] == "list":
# 列表里面套一个个的元组,每个元组包含url和权限组title
menu_permission_list.append((item["permissions__url"], item["permissions__group__title"])) print("menu_permission_list", menu_permission_list) # 注册到session中
request.session["menu_permission_list"] = menu_permission_list
注意:
(1)注册菜单权限:
# 注册菜单权限
permissions = user.roles.all().values("permissions__url", "permissions__group_id", "permissions__action",
"permissions__group__title").distinct()
其中permissions__group__title是跨三张表查询。
(2)在菜单权限列表中添加元组,每个元组包含url和权限组title信息。
menu_permission_list = [] # 菜单栏中权限列表:空列表
for item in permissions:
# item是里面的字典
if item["permissions__action"] == "list":
# 列表里面套一个个的元组,每个元组包含url和权限组title
menu_permission_list.append((item["permissions__url"], item["permissions__group__title"])) print("menu_permission_list", menu_permission_list)
(3)将菜单权限列表注册到session中:
# 注册到session中
request.session["menu_permission_list"] = menu_permission_list
2、自定义标签(inclusion_tag)
因为模板继承,只继承样式,不继承数据!所以需要用到自定义标签(inclusion_tag)。
在rbac项目下创建一个templatetags文夹。这个文件夹的名字必顺是templatetags来命名的。然后在此文件夹下自定义一个my_tags.py文件。
from django import template register = template.Library() @register.inclusion_tag("menu.html")
def get_menu(request):
# 获取当前用户应该放到菜单栏中的权限
menu_permission_list = request.session["menu_permission_list"] return {"menu_permission_list": menu_permission_list}
它会将返回数据传递给模板文件menu.html.
创建menu.html模板:
<div>
{% for item in menu_permission_list %}
<p class="menu_btn"><a href="{{ item.0 }}">{{ item.1 }}</a></p>
{% endfor %}
</div>
修改base.html模板:
<body> <div class="header">
<p>{{ user.name }}</p>
</div>
<div class="contain">
{% load my_tags %}
<div class="menu">
{% get_menu request %}
</div>
<div class="content">
{% block con%} {% endblock %}
</div>
</div> </body>
3、模板迁移及模板渲染规则
由于rbac是可插拔组件,因此可以将属于权限的模板文件迁移到rbac的app中。
创建rbac/templates文件夹,将users.html / roles.html / base.html / menu.html剪切到文件夹中。
django的render去渲染 .html 时,先到项目的 templates 下找,如果找不到再到App下templates 下找,
最后找不到才报错。
(1)如果多个App的templates 下的.html重名怎么办?
django 会根据注册的顺序显示!
解决办法:项目/rbac/templates/rbac/xxx.html
这时调用:return render(request, 'rbac/users.html', locals())
(2)templates 或者 templatetag 注意多个app下面 的文件名 有可能都会重名!
办法:就是 eg:/rbac/templates/rbac/xxx.html 或者不起重名
(3)同名.html文件查找顺序?
如果 base.html 在项目下有,在App下有,先找项目下的,找不到才找App,因此全局可以覆盖局部的!!
五、django路径自动添加
1、django路径添加现象及原理
知识点:路径自动添加问题:
http://127.0.0.1:8010/users
http://127.0.0.1:8010/users/
发现在浏览器浏览时,两个路径都可以正常访问到页面。这是因为浏览器发请求:django 发现之后,发了一个重定向的 url 加了一个 / 所以才能匹配上:path('users/', views.users),
2、路径配置
如果想让django不给浏览器发重定向。可以在setttings.py中添加:
APPEND_SLASH = False
在不添加这个配置时,django默认APPEND_SLASH的值为True,django会默认的加 / 发重定向。
ajax中的url和这里同理。
rbac——界面、权限的更多相关文章
- RBAC角色权限控制
RBAC角色权限控制 1. user (用户表) * 用户的基本信息(mid:用户信息id 如图) 2. node (节点表) * 页面(模块\控制器\方法) 3. role_node(角色.节点 ...
- ThinkPHP框架下基于RBAC的权限控制模式详解
这几天因为要做一个项目,需要可以对Web应用中通用功能进行封装,其中一个很重要的涉及到了对用户.角色和权限部分的灵活管理.所以基于TP框架自己封装了一个对操作权限和菜单权限进行灵活配置的可控制模式. ...
- rbac之 权限粒度控制到按钮级别
rbac之 权限粒度控制到按钮级别: 这里的意思就是 如果当前用户,没有这个权限. 那么这个相对应的这个按钮的权限, 就不应该展示.看都不能给看到. 思路: 为每一个权限,设置一个别名. 这里是这 ...
- 百万年薪python之路 -- RBAC角色权限设计
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联.简单地说,一个用户拥有若干角色,每一个角色拥有若干权限.这样,就构造成"用 ...
- Java生鲜电商平台-RBAC系统权限的设计与架构
Java生鲜电商平台-RBAC系统权限的设计与架构 说明:根据上面的需求描述以及对需求的分析,我们得知通常的一个中小型系统对于权限系统所需实现的功能以及非功能性的需求,在下面我们将根据需求从技术角度上 ...
- 基于thinkphp3.2.3开发的CMS内容管理系统(二)- Rbac用户权限
基于thinkphp3.2.3开发的CMS内容管理系统 thinkphp版本:3.2.3 功能: --分类栏目管理 --文章管理 --商品管理 --用户管理 --角色管理 --权限管理 --友情链接管 ...
- 基于RBAC实现权限管理
基于RBAC实现权限管理 技术栈:SpringBoot.SpringMVC RBAC RBAC数据库表 主体 编号 账号 密码 001 admin 123456 资源 编号 资源名称 访问路径 001 ...
- Spring Security实现基于RBAC的权限表达式动态访问控制
昨天有个粉丝加了我,问我如何实现类似shiro的资源权限表达式的访问控制.我以前有一个小框架用的就是shiro,权限控制就用了资源权限表达式,所以这个东西对我不陌生,但是在Spring Securit ...
- rbac - 界面、权限
一.模板继承 知识点: users.html / roles.html 继承自 base.html 滑动时,固定 position: fixed;top:60px;bottom:0;left:0;wi ...
随机推荐
- (转)取消目录与SVN的关联
第一种方法: 直接.逐级地删除目标目录中的隐藏属性的.svn目录 第二种方法: 如果用的是TortoiseSVN客户端,则先在另外一处建立一个新目录A,右键点住svn目录并拖动到A上松手,在弹出的菜单 ...
- C++_异常9-异常的注意事项
一.先讨论异常被引发后,可能导致的问题 意外异常: 如果它是在带异常规范的函数中引发的,则必须与规范列表中的某种异常匹配,否则为意外异常.在默认情况下,这将导致程序异常终止(虽然C++11摒弃了异常规 ...
- LeetCode记录之13——Roman to Integer
能力有限,这道题采用的就是暴力方法,也只超过了39%的用户.需要注意的就是罗马数字如果IXC的后一位比前一位大的采取的是减的方式. Given a roman numeral, convert it ...
- 一种简单快速的模板解析方法,活用with javascript版
//一种简单快速的模板解析方法,活用with var parseTpl = function( str, data ) { var tmpl = 'var __p=[];' + 'with(obj|| ...
- python (1) 还不是大全的小问题
1.pythone 获取系统时间 import datetime nowTime=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')#现在 pa ...
- CSS 单行 多行文本溢出显示省略号
单行文本 overflow: hidden; text-overflow:ellipsis; white-space: nowrap; 多行文本溢出显示省略号: <style type=&quo ...
- js 中的! 和 !! 的区别
Js中!的用法是比较灵活的,它除了做逻辑运算常常会用!做类型判断,可以用!与上对象来求得一个布尔值,1.!可将变量转换成boolean类型,null.undefined和空字符串取反都为false,其 ...
- spark SQL编程
1.编程实现将 RDD 转换为 DataFrame源文件内容如下(包含 id,name,age): 1,Ella,362,Bob,293,Jack,29 请将数据复制保存到 Linux 系统中,命名为 ...
- RabbitMQ之消息持久化
消息的可靠性是RabbitMQ的一大特色,那么RabbitMQ是如何保证消息可靠性的呢——消息持久化. 为了保证RabbitMQ在退出或者crash等异常情况下数据没有丢失,需要将queue,exch ...
- twitter storm学习 - 安装部署问题汇总
已经碰到的或者将来碰到的关于安装部署方面的问题以及解决方法,先挖个坑 1.提交的topology在admin界面上看emitted始终都是0,查看日志发现有如下错误: worker [ERROR] E ...