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