RBAC权限系统实现步骤-初版
一、创建并注册APP
1. 创建App: python mange.py startapp rbac 2. 注册APP:setting.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'web.apps.WebConfig', 'rbac.apps.RbacConfig' ]
二、设计表结构
from django.db import models # Create your models here. """ RBAC涉及到5张表 1. 用户表 (用户和角色是多对多的关系) 2. 角色表 (角色和权限是多对多的关系) 3. 权限表 4. 用户-角色表 5. 角色-权限表 """ class UserInfo(models.Model): """ 用户表 roles 字段: 用户和角色多对多字段,这个字段可以写在用户表或者角色表中,主要看场景是根据角色找用户还是根据用户找角色, 在这个场景中,我们根据用户找角色多,所以我们把ManyToManyField字段写在用户表中 null = True : 允许为空 blank = True: 在django Admin后台允许为空 """ username = models.CharField(max_length=32, verbose_name="用户名") password = models.CharField(max_length=64, verbose_name="密码") roles = models.ManyToManyField(to="Role", null=True, blank=True, verbose_name="用户角色") def __str__(self): """ 在Django Admin中显示数据名称 :return: """ return self.username class Meta: """ 在Django Admin中显示中文表名 """ verbose_name = "用户表" verbose_name_plural = verbose_name class Role(models.Model): """ 角色权限表 Permissions: 角色和权限URL是多对多的关系,在这个场景中我们使用角色查找URL,所以我们把ManyToManyField写在角色表中 """ name = models.CharField(max_length=36, verbose_name="角色名称") Permissions = models.ManyToManyField(to="Permission", verbose_name="权限URL") def __str__(self): return self.name class Meta: verbose_name = "角色表" verbose_name_plural = verbose_name class Permission(models.Model): """ 权限表 保存需要控制的URL """ name = models.CharField(max_length=16, verbose_name="UR名称") url = models.CharField(max_length=255, verbose_name="URL路径") is_menus = models.BooleanField(default=False, verbose_name="是否可作为菜单?") icon = models.CharField(max_length=255, verbose_name="菜单图标", null=True, blank=True) def __str__(self): return self.url class Meta: verbose_name = "权限表" verbose_name_plural = verbose_name
三、创建表结构
python manage.py makemigrations
python manage.py migrate
四、使用Django Admin创建初始数据
1. python manage.py createsuperuser 2. 配置Django 后台中文(settings.py) LANGUAGE_CODE = 'zh-hans' 3. 注册表到Admin中(admin.py) from django.contrib import admin from rbac import models # Register your models here. admin.site.register(models.UserInfo) admin.site.register(models.Role) # 自定义一个权限的管理类 class PermissionAdmin(admin.ModelAdmin): # 告诉Django admin在页面上展示我这张表的哪些字段 list_display = ["name", "url", "is_menus", "icon"] # 在列表页面支持直接修改的字段 list_editable = ["url", "is_menus", "icon"] admin.site.register(models.Permission, PermissionAdmin) 4. 录入基础数据
五、写登录页面
#!/usr/bin/env python3 from django.shortcuts import render, redirect from rbac import models from rbac.services import permission def login(request): """ 用户登录页面 1. Get请求 1. 返回登录页面 2. Post请求 1. 拿到页面通过post传过来的用户名和者密码 2. 使用orm进行过滤查找 1. 如果能找到值,则说明登录成功 1. 登录成功以后调用rbac函数初始化 2. 初始化的主要功能是获取用户的权限和菜单保存到session中 3. 跳转到客户列表页面 2. 登录失败,返回错误信息给页面展示 :param request: :return: """ msg = {"msg": ""} if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user_obj = models.UserInfo.objects.filter(username=username, password=password).first() if user_obj: permission.init_permission(request, user_obj) return redirect("/customer/list/") else: msg["msg"] = "用户名或者密码错误!" return render(request, "login.html", locals())
六、写初始化函数(init_permission)
函数功能:根据登录的用户对象取到对象的权限和菜单保存到session中 #!/usr/bin/env python3 """ 用户登录成功以后,获取用户的的权限进行 """ from django.conf import settings def init_permission(request, user_obj): """ 初始化rbac 1. 根据用户对象,取到用户对应的角色,根据角色在取到对应的权限。最后distinct做一个去重处理 2. permission_list 定义一个用来存储用户权限的列表 3. menus_list 定义一个用来存储用户菜单的列表 4. 循环ret,添加权限和菜单到对应的列表 5. 保存权限列表和菜单列表到session中 备注:使用settings来存session的key方便在其他模块中调用 :param request: 用户请求对象 :param user_obj: 用户orm对象 :return: """ ret = user_obj.roles.all().values( "Permissions__name", "Permissions__url", "Permissions__is_menus", "Permissions__icon" ).distinct() permission_list = [] menus_list = [] for item in ret: permission_list.append({"Permissions__url": item["Permissions__url"]}) # 添加到权限列表 if item["Permissions__is_menus"]: # 如果当前循环的权限可以作为菜单展示 menus_list.append({ # 把当前权限的信息添加到菜单列表 "name": item["Permissions__name"], "icon": item["Permissions__icon"], "url": item["Permissions__url"] }) request.session[settings.PERMISSION_SESSION_KEY] = permission_list request.session[settings.MENU_SESSION_KEY] = menus_list
七、在登录视图中调用init_permission
#!/usr/bin/env python3 from django.shortcuts import render, redirect from rbac import models from rbac.services import permission def login(request): """ 用户登录页面 1. Get请求 1. 返回登录页面 2. Post请求 1. 拿到页面通过post传过来的用户名和者密码 2. 使用orm进行过滤查找 1. 如果能找到值,则说明登录成功 1. 登录成功以后调用rbac函数初始化 2. 初始化的主要功能是获取用户的权限和菜单保存到session中 3. 跳转到客户列表页面 2. 登录失败,返回错误信息给页面展示 :param request: :return: """ msg = {"msg": ""} if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") user_obj = models.UserInfo.objects.filter(username=username, password=password).first() if user_obj: permission.init_permission(request, user_obj) return redirect("/customer/list/") else: msg["msg"] = "用户名或者密码错误!" return render(request, "login.html", locals())
八、自定义中间件
1. 在rbac APP下创建middleware中间库目录 2. 创建中间件rbac.py """ 自定义RBAC中间件 功能描述: 根据用户角色实现权限控制 """ from django.utils.deprecation import MiddlewareMixin from django.shortcuts import render, redirect, HttpResponse import re from django.conf import settings class RBACMiddleware(MiddlewareMixin): def process_request(self, request): """ 自定义中间件 1. 中间件的描述 1. 执行时间 在执行视图函数之前执行 2. 执行顺序 按照注册的顺序执行 3. 参数和返回值 1. request参数和视图函数中是同一个对象 2. 返回值: 1. 返回None:请求继续往后执行 2. 返回响应对象:请求就结束了,要返回响应了 2. 取到用户的url 1. 循环白名单 2. 判断用户当前访问的URL是否在白名单中 3. 如果在白名单中则返回None代码继续往后执行 3. 取到用户的访问权限 1. 如果没有取到登录时存的session,则说明用户没有登录,跳转到登录页面 :param request: :return: """ url = request.path_info for i in settings.PERMISSION_WHITE_URL: ret = "^{}$".format(i) if re.match(ret, url): return None user_url = request.session.get(settings.PERMISSION_SESSION_KEY) if not user_url: return redirect("/login/") for i in user_url: ret = "^{}$".format(i["Permissions__url"]) if re.match(ret, url): return None else: return HttpResponse("没有权限方法") 3. 注册中间件settings.py 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.middleware.rbac.RBACMiddleware', ] 4. 权限控制已经完成
九、开始配置菜单
方法1: 直接在菜单html修改菜单html为: {% for menu in request.session.menu_list %} <a href="{{ menu.url }}" class="active"> <span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.name }}</a> {% endfor %} 方法2: 使用模版语言的filter 1. 修改菜单html {% load view %} {% show_menu request %} 2. 创建app/templatetags/view.py from django import template from django.conf import settings register = template.Library() @register.inclusion_tag(filename="my_menu.html") def show_menu(request): menu_list = request.session.get(settings.MENU_SESSION_KEY) return {"menu_list": menu_list} 3. 创建app/templates/my_menu.html <div class="static-menu"> {% for menu in menu_list %} <a href="{{ menu.url }}" class="active"> <span class="icon-wrap"><i class="fa {{ menu.icon }}"></i></span> {{ menu.name }}</a> {% endfor %} </div>