权限管理
创建一个rbac和app的应用,这个rbac主要是用来存放权限的,全称叫做基于角色权限控制
一、先看配置文件合适不,给创建的rbac在配置文件里面设置一下
找到INSTALLED_APPS=【'rbac'】
二、设计表结构
models中创建类:五个类,七张表
角色表:
用户表:
权限表:
组表:
菜单表:
角色表和权限表是多对多的关系(一个角色可以有多个权限,一个权限可以对应多个角色)
用户表和角色表是多对多的关系(一个用户可以有多个角色,一个角色有多个用户)
所以有会多生成两张关系表
一个菜单下面有多个组
一个组下面有多个菜单
一个菜单下面有多个权限
from django.db import models # Create your models here. class Role(models.Model): ''' 角色表 ''' title = models.CharField(max_length=32,verbose_name="角色名") permissions = models.ManyToManyField(to="Permission",verbose_name="具有的所有权限", blank=True) # 建立用户表和角色表的多对多关系 def __str__(self): return self.title class Meta: verbose_name_plural = "角色表" class Group(models.Model): caption = models.CharField(max_length=32,verbose_name="组名称") menu = models.ForeignKey(to="Menu",verbose_name="所属菜单",default=1,related_name="menu") class Menu(models.Model): title = models.CharField(max_length=32) class Permission(models.Model): ''' 权限表 ''' title = models.CharField(max_length=32,verbose_name="标题") url = models.CharField(max_length=64,verbose_name="带正则的URL") # is_mune = models.BooleanField(verbose_name="是否是菜单",default=0) menu_gp = models.ForeignKey(verbose_name="组内菜单",to="Permission",blank=True,null=True) #自关联 #主页就可以设置为菜单,当点击菜单的时候才可以做具体的操作 codes = models.CharField(max_length=32,verbose_name="代码",default=1) group = models.ForeignKey(to="Group",verbose_name="所属组",null=True) #新添加的字段记得设置默认值 def __str__(self): return self.title class Meta: '''中文显示''' verbose_name_plural = "权限表" class UserInfo(models.Model): ''' 用户表 ''' username = models.CharField(max_length=32,verbose_name="用户名") password = models.CharField(max_length=64,verbose_name="密码") email = models.CharField(max_length=32,verbose_name="邮箱") roles = models.ManyToManyField(to="Role",verbose_name="具有的所有角色",blank=True) #建立用户和角色的多对多关系 def __str__(self): return self.username class Meta: verbose_name_plural = "用户表"
三、通过django admin录入权限数据
- 先创建一个超级用户
- 用户名 root
- 密码 zhy123456
- 在admin.py 中
from rbac import models
admin.site.register(models.Permission)
admin.site.register(models.Role)
admin.site.register(models.UserInfo)
这样的话上去的是英文的,如果你想让中文显示就在类中加一个类
class Meta:
verbose_name_plural = "权限表"
- 当你给关联字段录入数据的时候会有错误提示,那么在类中你的那个关联字段在加一个属性blank = True 可以为空
permissions = models.ManyToManyField(to="Permission",verbose_name="具有的所有权限", blank=True)
四、编写登录
用户登录后:
获取当前用户具有的所有角色
获取当前用户具有的所有权限(并且去重)
#获取当前的用户对象 user = models.UserInfo.objects.filter(username=name,password=password).first() #获取当前用户的所有角色user.roles.all() #获取当前用户的所有权限 permission_list = user.roles.all().values("permissions__title","permissions__url","permissions__is_mune").distinct() print(permission_list)
登录成功之后初始化---把所有的url都拿出来并且保存到session里
1、可以建一个server的包,在里面建一个初始化的init_perssion.py文件
2、定义函数
#!usr/bin/env python # -*- coding:utf-8 -*- from rbac import models from day7权限管理 import settings def init_permission(user,request): #获取当前的用户对象 ''' 获取所有权限中的url并放在session中 :param user: :param request: :return: ''' #user.roles.all()获取当前用户的所有角色 #获取当前用户的所有权限 permission_list = user.roles.all().values( "permissions__id", "permissions__title", #用户列表 "permissions__url", #url "permissions__menu_gp", #组内菜单id,如果为null表示是菜单 "permissions__codes", "permissions__group_id", "permissions__group__menu__title", #菜单名称 "permissions__group__menu__id" #菜单id ).distinct() print("-----------------",permission_list) #菜单相关 sub_permisson_list = [] for item in permission_list: tpl = { "id":item["permissions__id"], "title":item["permissions__title"], "url":item["permissions__url"], "menu_group_id":item["permissions__group_id"], "menu_id":item["permissions__group__menu__id"], "menu_title":item["permissions__group__menu__title"] } sub_permisson_list.append(tpl) request.session[settings.PERMISSION_MENU_KEY] = sub_permisson_list # 1、去掉不是菜单的url # 菜单操作 # menu_list = [] # for item in permission_list: # if not item["permissions__is_mune"]: # 如果不是菜单就继续 # continue # else: # 否则是菜单的话就把菜单添加到列表里面 # tpl = { # "menu_id": item["permissions__group__menu__id"], # "menu_title": item["permissions__group__menu__title"], # "title": item["permissions__title"], # "url": item["permissions__url"], # "active": False # } # menu_list.append(tpl) # request.session[settings.PERMISSION_MENU_KEY] = menu_list #吧所有的菜单都放在session里面 # print("xcvxvxv", menu_list) # 权限相关 result = {} for item in permission_list: group_id = item["permissions__group_id"] url =item["permissions__url"] code = item["permissions__codes"] if group_id in result: #如果在说明url和code已经生成了 result[group_id]["codes"].append(code) result[group_id]["urls"].append(url) else: #如果不在就添加进去 result[group_id] ={ "codes":[code,], "urls":[url] } # print(result) #吧所有权限中的url字典放到session中 request.session[settings.PERMISSION_URL_DICT_KEY] = result # # 打印的结果如下 # result = { # 1: { # "codes": ["list", "add", "del", "edit"] # "urls": [ # "/userinfo/", # "/userinfo/add" , # "/userinfo/del(d+)/ ", # "/userinfo/edit(d+)/ ", # ] # }, # 2: { # "codes": {"list", "add", "del", "edit"} # "urls": [ # "/order", # "/order/add" , # "/order/del(d+)/ ", # "/order/edit(d+)/ ", # ] # } # }
五、中间件
- 在setting里设置白名单(不用权限就可以访问)
#白名单 VALID_URL = [ "/login/", "/admin.*/", "/index/" ] # ==================rabc============== PERMISSION_URL_DICT_KEY="permissions_url_dict" PERMISSION_MENU_KEY = "menu_list"
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'rbac.middlewears.rbac.Middle', ]
- 获取当前的url请求
- 获取session保存的权限信息
- 循环url进行正则匹配,如果匹配成功就有权访问,不成功就无法访问
用中间件的时候记得要在settings里面配置一下:
#!usr/bin/env python # -*- coding:utf-8 -*- import re from django.shortcuts import render,HttpResponse,redirect 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): #获取当前的url请求 current_url = request.path_info #拿到当前的路径 # print(request.path,current_url) #获取Session中保存当前用户的权限, # request.session.get("permissions_url_list") # 如果当前的路径和session里面保存的url一样就break了,如果不一样就说明无权访问 for url in settings.VALID_URL: # print(url,current_url) if re.match(url,current_url): return None #如果url是表名单白名单里面的,就让直接走后面的 permission_dict = request.session.get(settings.PERMISSION_URL_DICT_KEY) #在初始化的时候把url以字典的显示存在了session里面,现在获取的也就是一个字典了 # print("==========",permission_dict) flag = False for group_id,code_url in permission_dict.items(): for url in code_url["urls"]: regax = "^{0}$".format(url) # print(regax,current_url) if re.match(regax,current_url): #match只要是..开头的都能匹配到,多以的加个^和$符 request.permission_code_list=code_url["codes"] flag = True break if flag: #跳出外层循环 break if not flag: return HttpResponse("无权访问") def process_response(self,request,response): return response