Django权限管理之初步完整版
项目背景:这是一个权限管理系统(给一些角色和他们的权限指URL和页面可以删除的按钮比如:增删改查)
使用到了中间件,和初始化权限,使用了admin的后台管理系统。
我们这个是基于角色的权限访问控制(Role-Based Access Control)做一个组件。
首先建立一个项目工程里面有另个应用:app01与rbac,
我们在rbac中model中建立一些数据类型代码如下:
from django.db import models # Create your models her class Menu(models.Model):#定义一个菜单组 """菜单组""" title=models.CharField(max_length=32) class Group(models.Model):#权限组(用户组与权限组) """权限组""" caption=models.CharField(verbose_name="组名称",max_length=32)#组的名称 menu=models.ForeignKey(verbose_name="所属菜单",to="Menu",default=1)# class Permission(models.Model):#权限表 """权限表""" url=models.CharField(max_length=64,verbose_name="URL")#权限表的url title=models.CharField(max_length=64,verbose_name="标题")#权限表的名字 menu_gp=models.ForeignKey(verbose_name="组内菜单",to="Permission",null=True,blank=True)#自关联 ,blank是在admin里可以设置为空,null是设置数据库的字段可以为空。 code=models.CharField(verbose_name="代码",max_length=16)#定义代码 group=models.ForeignKey(verbose_name="所属组",to="Group",null=True)#所属组 class Meta:#在admin里面显示表明 verbose_name_plural="权限表" def __str__(self):#打印名字 return self.title class Userinfo(models.Model):#用户组 """用户表""" username=models.CharField(verbose_name="名字",max_length=32)#用户的名字 password=models.CharField(verbose_name="密码",max_length=32)#用户的password user_role=models.ManyToManyField(verbose_name="所有的角色",to="Role",blank=True)#和角色进行多对多关联 class Meta:#在admin里面显示表明 verbose_name_plural="用户表" def __str__(self): return self.username class Role(models.Model):#角色名字 """角色表""" title=models.CharField(verbose_name="职位",max_length=32)#角色的名字 role_permission=models.ManyToManyField(verbose_name="权限",to="Permission",blank=True) class Meta: verbose_name_plural="角色表" def __str__(self): return self.title
再rbac中的admin中填入需要在生成的数据代码如下:
from django.contrib import admin # Register your models here. from . import models#用djangoadmin时一定要在这里写东西 admin.site.register(models.Permission) admin.site.register(models.Userinfo) admin.site.register(models.Role)
中间件的代码如下:
import re from django.shortcuts import redirect,HttpResponse from django.conf import settings class MiddlewareMixin(object):#这个是中间件 def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response class Middle(MiddlewareMixin): def process_request(self,request): #1 .获取当前请求的URL # request.path_info #2. 获取Session中保存当前用户的权限 # request.session.get("permission_url_list") current_url=request.path_info#这个是获取的当前请求的路径 print("当前请求的路径",current_url)#获取当前的url for url in settings.VALID_URL:#这里设置白名单 if re.match(url,current_url):#匹配是否在白名单这个不要格式化 return None#返回None返回为空就可以经过下一个中间 permission_dict = request.session[settings.PERMISSION_URL_DICT_KEY]#获取权限url和这个页面的某些权限 print("有这些权限URl",permission_dict) if not permission_dict: return redirect("/login/")#如果没有登录就直接返回登录页面 flag = False#设置一个flag for group_id ,code_url in permission_dict.items():#字典循环获取值用 字典名.items() for db_url in code_url["urls"]:#循环取里面的url regax="^{0}$".format(db_url)#对取出的url进行格式化 if re.match(regax,current_url):#通过匹配找出是否在这里面 request.permission_code_list=code_url["codes"] print("这里是获取的是单个页面里面的权限",request.permission_code_list) flag=True#如果匹配成功直接跳出去 break if flag: break if not flag:#如果没有跳出说明他没有权限 return HttpResponse("你没有权限")
初始化权限的代码:
from django.conf import settings def init__perimission(user,request):#初始化权限 perimission_list = user.user_role.values("role_permission__id",#权限的id "role_permission__title",#权限的名字 "role_permission__url",#权限的url # "role_permission__is_menu", "role_permission__code",#权限名字的代码 "role_permission__menu_gp_id",#组内菜单的id(一个组的菜单id) "role_permission__group_id",#所属的组 "role_permission__group__menu_id",#菜单id "role_permission__group__menu__title",#菜单的名字 ).distinct()#这里获取的是的个人的权限与明细 print("这就是最终拿到的个人明细",perimission_list) # 更改菜单(这个菜单是在同一个组内的菜单不变始终存在) sub_perimission_list=[]#先定义一个列表 for item in perimission_list:#首先是循环里面所有的数据 tpl={ "id":item["role_permission__id"],#获取id是权限的id "title":item["role_permission__title"],#获取的是权限的名字 "url":item["role_permission__url"],#权限的url "menu_gp_id":item["role_permission__menu_gp_id"],#这是组内菜单的id "menu_id":item["role_permission__group__menu_id"],#这是菜单的id "menu_title":item["role_permission__group__menu__title"]#这是菜单的名字 } sub_perimission_list.append(tpl)#加入到列表中 request.session[settings.PERMISSION_MEN_KEY] = sub_perimission_list#复制到session "-----------以上是菜单的处理" #权限的处理 result ={}#先定义一个字典 for item in perimission_list:#从这个数据里循环数据 group_id=item["role_permission__group_id"]#所属组 code=item["role_permission__code"]#一个页面里的有哪些的权限 url=item["role_permission__url"]#权限对应的url if group_id in result: result[group_id]["codes"].append(code)#加入权限代码 result[group_id]["urls"].append(url)#加入权限url else: result[group_id]={ "codes":[code,], "urls":[url,], } request.session[settings.PERMISSION_URL_DICT_KEY]=result#赋值权限
数据库的持久化后接着用admin的后台填一些数据:
我们自己设置了关于网页的自定义标签:名字是rbac
import re from django.template import Library from django.conf import settings register = Library() #自定义的标签 @register.inclusion_tag("xxxx.html")#从别的地方拿到数据 def menu_html(request): menu_list = request.session[settings.PERMISSION_MEN_KEY]#拿到菜单的数据 current_url = request.path_info#当前的url menu_dict = {}#定义一个字典 for item in menu_list:#先拿到所有的菜单 if not item["menu_gp_id"]:#判断是否是菜单 menu_dict[item["id"]]=item#如果是就把数据赋值给他 for item in menu_list:#这个是循环所有的数据进行url的匹配 给要匹配的url匹配出要显示哪个页面 regex="^{0}$".format(item["url"])#先把数据进行格式化 if re.match(regex,current_url):#如果匹配到数据 menu_gp_id=item["menu_gp_id"]#最内菜单id if menu_gp_id:#如果是数字就给这个数字加上“active”=True menu_dict[menu_gp_id]["active"]=True else: menu_dict[item["id"]]["active"]=True#如果不是的当前的加上“active”=True result={}#先定义一个字典 for item in menu_dict.values():#这是字典的取值 active=item.get("active")#获取它的active menu_id=item["menu_id"]#获取菜单值 if menu_id in result: result[menu_id]["children"].append({"title":item["title"],"url":item["url"],"active":active}) if active: result[menu_id]["active"]=True#获取菜单的active用来判断是否加上hide else: result[menu_id]={ "menu_id":item["menu_id"], "menu_title":item["menu_title"], "active":active,#判断 "children":[ { "titel":item["title"], "url":item["url"], "active":active #这个的话是用来判断是否需要标红的 } ] } return {'menu_dict':result}#返回用来显示字典的
xxxx代码:
{% for foo,item in menu_dict.items %} <div class="item"> <div class="item-title">{{ item.menu_title }}</div> {% if item.active %} <div class="item-permission"> {% else %} <div class="item-permission hide"> {% endif %} {% for v in item.children %} {% if v.active %} <a href="{{ v.url }}" class="active">{{ v.titel }}</a> {% else %} <a href="{{ v.url }}">{{ v.titel }}</a> {% endif %} </div> {% endfor %} </div> {% endfor %}
这是应用app01里调用代码:名字是layout是模板
{% load rbac %}<!--这个是继承自定义标签的--> <!--这个是模板以后都从这个继承--> <!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link rel="stylesheet" href="/static/rbac/rbac.css"><!--这个是导入css样式--> </head> <body> <div class="header"> {% block header %} <h1>欢迎回来</h1> {% endblock header %} </div> <div class="siderbar"> {% block siderbar %} {% menu_html request %}<!--这个是产生菜单的样式--> {% endblock siderbar %} </div> <div class="content"> {% block content %} {% endblock content %} </div> <script src="/static/jquery-3.2.1.min.js"></script> <script src="/static/rbac/rabc.js"></script><!--这个是导入js样式--> </body> </html>
别的页面也是继承这个页面:
{% extends 'layout.html' %} {% block content %} {% if page_permission.has_add %} <a href="/userinfo/add/">添加用户11</a> {% endif %} <table> {% for user in userlist %} <tr> <td>{{ user.id }}</td> <td>{{ user.name }}</td> {% if page_permission.has_edit %} <td><a href="">编辑</a></td> {% endif %} {% if page_permission.has_del %} <td><a href="">删除</a></td> {% endif %} </tr> {% endfor %} </table> {% endblock %}
在判断一个权限是否属于时可以建立一个类:
class BasePagePermission(object):#有某一个更改的权限 def __init__(self,code_list): self.code_list=code_list def has_add(self): if "add" in self.code_list: return True def has_edit(self): if "edit" in self.code_list: return True def has_del(self): if "del" in self.code_list: return True
项目app01中的view中的代码:
import re from django.shortcuts import render,redirect,HttpResponse from rbac import models from rbac.server.init_permission import init__perimission def login(request): #登录判断 if request.method=="GET": return render(request,"login.html") else: name=request.POST.get("name") pwd=request.POST.get("pwd") user=models.Userinfo.objects.filter(username=name,password=pwd).first() print("用户名",user) if not user: return render(request,"login.html") init__perimission(user,request)#初始化权限 return redirect("/index/")#返回权限 def index(request): return render(request,"index.html") class BasePagePermission(object):#有某一个更改的权限 def __init__(self,code_list): self.code_list=code_list def has_add(self): if "add" in self.code_list: return True def has_edit(self): if "edit" in self.code_list: return True def has_del(self): if "del" in self.code_list: return True def userinfo(request): pagepermission= BasePagePermission(request.permission_code_list)#查看有哪些表有哪些删除或者修改的权限 data_list=[ { "id":1,"name":"xx1" }, { "id":2,"name":"xx1" }, { "id":3,"name":"xx1" }, { "id":4,"name":"xx1" }, { "id":5,"name":"xx1" } ] return render(request,"userinfo.html",{"userlist":data_list,"page_permission":pagepermission})#返回的是数据和是不是有这些权限的布尔值 def userinfoadd(request): userinfo_permission = BasePagePermission(request.permission_code_list) return render(request,"userinfo_add.html",{"gepermissionon":userinfo_permission}) class OrderPermission(BasePagePermission): def has_order(self): if "order" in self.code_list: return True def userinfodel(request,nid): return HttpResponse("删除人员信息") def userinfoedit(request,nid): return HttpResponse("编辑人员信息") def orderinfo(request): order_permission = BasePagePermission(request.permission_code_list) return render(request,"orderinfo.html") def orderinfoadd(request): return HttpResponse("增加订单") def orderinfodel(request,nid): return HttpResponse("删除订单") def orderinfoedit(request,nid): return HttpResponse("编辑订单")
用到的js代码:
$(function () { $('.item-title').click(function () { // if($(this).next().hasClass('hide')){ // $(this).next().removeClass('hide') // }else{ // $(this).next().addClass('hide') // } // alert(111) $(this).next().toggleClass('hide'); }) });
中间件的代码结构:
在这次项目中我们要在settings里面配置白名单和两个全局变量。
注:如果把组件导入到另一项目中,一定要在views中login函数中导入组件中的的models。源码放在了78天准备练习中