rbac——界面权限
Posted xiugeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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、重写inital_session(user, request)函数
# -*- coding:utf-8 -*- __author__ = ‘Qiushi Huang‘ def inital_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.editper.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 %}
显示效果:
四、总结
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("没有访问权限!")
以上是关于rbac——界面权限的主要内容,如果未能解决你的问题,请参考以下文章