一、创建并注册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>