rbac: Role_Based Access Control,基于角色的权限控制
权限:一个包含正则表达式 的url就是一个权限
目录结构:
rbac这个app中的文件代码如下:
rbac/models.py
from django.db import models # Create your models here. class User(models.Model):
# 这个User要和 app 中的 用户信息表 一对一关联; from rbac.models import * name = 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=128) actions = models.CharField(max_length=32) # 用于标识“增删改查”某一操作 --- "add","delete","edit","list" group = models.ForeignKey(to="PermissionGroup",on_delete=models.CASCADE) # 能够避免页面渲染时,判断 <a>标签是否存在时的 url 带有表名 """ 所要得到的数据结构 permission_dict : { 1:{ url:[...], actions:[...] } 2:{ url:[...], actions:[...] }, ... } # 字典中的数字键代表 permission_group,即权限分类(角色或者表名分类) """ def __str__(self): return self.title class PermissionGroup(models.Model): """ 作用:标识哪个权限属于哪个组(或者说是哪个表的增删改查) """ title = models.CharField(max_length=32) def __str__(self): return self.title
rbac/service/register_permissions.py
def initiate_permissions_session(request,user): """ 登陆后把权限注册到session中 """ # 方式一:只包含 url 的列表 """ permissions = user.roles.all().values("permissions__url").distinct() # 取出当前用户的所有权限(QuerySet;要去重) permission_list = [] for item in permissions: permission_list.append(item["permissions__url"]) request.session["permission_list"] = permission_list # 把权限列表注册到session 中,以后直接从 session 中去取 """ # 方式二:字典 permissions = user.roles.all().values("permissions__url","permissions__actions","permissions__group_id").distinct() permissions_dict = {} for item in permissions: pgid = item.get("permissions__group_id") if pgid not in permissions_dict: permissions_dict[pgid] = { "actions":[item["permissions__actions"]], "urls":[item["permissions__url"]] } else: permissions_dict[pgid]["actions"].append(item["permissions__actions"]) permissions_dict[pgid]["urls"].append(item["permissions__url"]) request.session["permissions_dict"] = permissions_dict """ 方式二得到的 permissions_dict 数据结构形式: { 1:{ url:[...], actions:[...] } 2:{ url:[...], actions:[...] }, ... } """ """ 登陆验证成功后要调用这个函数 """
rbac/service/rbac.py
from django.utils.deprecation import MiddlewareMixin import re from django.shortcuts import redirect,HttpResponse class PermissionValid(MiddlewareMixin): """ 通过中间件校验用户是否有权限访问某个url """ def process_request(self,request): current_path = request.path url_white_list = ["/login/","/reg/","/admin/.*"] # /admin/.*" 表示 所有 以 admin 开关的url # 先校验当前的 url 是否在 白名单中 (url_white_list中有正则,不能直接用 in 判断) for url in url_white_list: ret = re.match(url,current_path) if ret: return None # 通过校验 # 再校验当前用户是否已经登陆(根据具体的登陆验证逻辑来重构这块的代码) if request.user.is_anonymous: return redirect("/login/") # 跳转到登陆页面 """ def reg(request,current_path): 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 return flag #校验权限1(permission_list) permission_list = request.session.get("permission_list",[]) # ['/users/', '/users/add', '/users/delete/(\d+)', 'users/edit/(\d+)'] flag=reg(request,current_path) if not flag: return HttpResponse("没有访问权限!") return None """ # 校验权限2:(permission_dict) 最后判断用户是否有当前url 的权限 permissions_dict = request.session["permissions_dict"] for item in permissions_dict.values(): for url in item["urls"]: ret = "^%s$"%url # 为了让 url 和 current_path 完全匹配,需要在其前后加上 ^$ ret = re.match(url,current_path) if ret: request.actions = item["actions"] # 如果匹配成功,就把该当前用户对该表的所有能进行的操作添加到 request中; # Permission表中 actions & group字段,和 PermissionGroup这个表都是为了这一步 return None return HttpResponse("您没有这个url的权限!")
rbac组件小结:
1 权限粒度控制 2 3 简单控制: 4 {% if "users/add" in permissions_list%} 5 6 7 摆脱表控制 8 9 10 更改数据库结构 11 class Permission(models.Model): 12 title=models.CharField(max_length=32) 13 url=models.CharField(max_length=32) 14 15 action=models.CharField(max_length=32,default="") 16 group=models.ForeignKey("PermissionGroup",default=1) 17 def __str__(self):return self.title 18 19 20 21 class PermissionGroup(models.Model): 22 title = models.CharField(max_length=32) 23 24 def __str__(self): return self.title 25 26 27 登录验证: 28 permissions = user.roles.all().values("permissions__url","permissions__group_id","permissions__action").distinct() 29 30 构建permission_dict 31 32 permissions: 33 [ 34 35 {'permissions__url': '/users/add/', 36 'permissions__group_id': 1, 37 'permissions__action': 'add'}, 38 39 {'permissions__url': '/roles/', 40 'permissions__group_id': 2, 41 'permissions__action': 'list'}, 42 43 {'permissions__url': '/users/delete/(\d+)', 44 'permissions__group_id': 1, 45 'permissions__action': 'delete'}, 46 47 {'permissions__url': 'users/edit/(\d+)', 48 'permissions__group_id': 1, 49 'permissions__action': 'edit'} 50 ] 51 52 permission_dict 53 54 55 { 56 57 1: { 58 'urls': ['/users/', '/users/add/', '/users/delete/(\d+)', 'users/edit/(\d+)'], 59 'actions': ['list', 'add', 'delete', 'edit']}, 60 61 2: { 62 'urls': ['/roles/'], 63 'actions': ['list']} 64 65 } 66 67 68 69 中间价校验权限: 70 permission_dict=request.session.get("permission_dict") 71 72 for item in permission_dict.values(): 73 urls=item['urls'] 74 for reg in urls: 75 reg="^%s$"%reg 76 ret=re.match(reg,current_path) 77 if ret: 78 print("actions",item['actions']) 79 request.actions=item['actions'] 80 return None 81 82 return HttpResponse("没有访问权限!")