一、創建並注冊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>