权限设计

权限组件开发

为什么是什么?

1.为什么要开发关于权限相关的系统?
在现实生活中,就有很多的关于权限的场景,比如:在超市,店长的权限永远比员工权限大。在公司,老板的权限永远比经理大。老板可以知道全部员工的工资多少,而经理只能知道自己部门的工资。这些就是权限的体现。
2.权限是什么?
在web系统中一个url就是一个权限,那么一个人能访问的url,那么就有多少个权限

权限的表结构

第一版权限结构

用户表
ID User
1 小明
2 小张
3 小李
用户与权限(多对多表)
ID user_id(与user表形成1对多关系) Permissions_id(与权限表形成1对多关系)
1 1 1
1 2 1
1 3 1
1 1 2
权限表(url表)
ID Url Shows
1 /user/index 用户首页
2 /home 网站首页
3 /user/del 用户删除
实现: '用户' 直接对应 '权限',通过'第三张表的多对多关系' 绑定对应的权限关系
当前实现了对权限的关联,但是存在弊端!
缺点与弊端:
1.如果有新来的用户需要权限,需要在 关联表中 进行11条的设置 # 权限建立麻烦
2.如果对某些用户需要修改权限,那么需要找出这类用户在 关联表中 进行增删改权限 # 修改权限麻烦
3.如果权限表中得权限是大量的(1000个),用户的体量也很大(50000个) 那么每一次修改需要花费大量时间 # 面多权限量大用户量大情况进行修改不明智不可取

第二版权限结构

用户表
ID User
1 小明
2 小张
3 小李
4 小王
用户表 and 角色表(多对多关系)
ID user_id role_id
1 1 1(人事经理权限)
2 2 2(部门经理权限)
3 3 2(部门经理权限)
4 4 3(普通员工权限)
角色表
ID role_name
1 人事经理
2 部门经理
3 员工
角色 and 权限(多对多关系)
ID role_id permissions_id
1 1 1,2,3,4,5(角色:人事经理有5条权限)
2 2 1,2,5(角色:部门经理有3条权限)
3 3 1,2(角色:员工有2条权限)
权限表(url表)
ID Url Shows
1 /user/index 用户首页
2 /home 网站首页
3 /user/del 用户删除
4 /user/update 用户修改
5 /user/insert 用户添加
5 /user/select 用户查询
RBAC:基于角色控制权限管理
优点:
1.可以通过角色设置权限(1个角色多个权限)
2.如果用户可以拥有多个角色(有多少角色就有多少权限)
3.如果新用户添加,那么直接添加对应的角色即可
4.如果需要修改部分用户的权限,可以创建新的角色绑定权限赋予用户,修改这部分用户的角色权限。
5.可以通过控制角色权限,而控制用户拥有的权限方便管理

在Django中进行创建表结构

from django.db import models
__all__ = ['Permissions', 'Role', 'User']
class Permissions(models.Model):
class Mate:
db_table = 'Permissions'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128,verbose_name='标题')
url = models.CharField(max_length=255, verbose_name='正则模式下的url')
def __str__(self):
return self.title
class Role(models.Model):
class Mate:
db_table = 'Role'
id = models.AutoField(primary_key=True)
role_name = models.CharField(max_length=255, verbose_name='角色名称')
permissions = models.ManyToManyField(to='Permissions', db_table='Role_Permissions', verbose_name='对多对权限角色报绑定字段')
def __str__(self):
return self.role_name
class User(models.Model):
class Mate:
db_table = 'User'
id = models.AutoField(primary_key=True)
user_name = models.CharField(max_length=128, verbose_name='账户')
password = models.CharField(max_length=128, verbose_name='密码')
user_email = models.CharField(max_length=128, verbose_name='邮箱')
role = models.ManyToManyField(to='Role', db_table='User_Role', verbose_name='多对多用户与角色绑定字段')
def __str__(self):
return self.user_name

基本权限控制

1.用户登录页面需不需要进行权限控制? # 不需要
2.用户在首页时需不需要进行权限控制? # 不需要
大致的执行流程
1.用户登录[判断用户的密码和账户,同时从数据库中获取权限的信息]
2.将权限的信息存储到session或者redis中[注意细节使用redis存储]
# 注意:
1.如果存储到session中,用户同时登录,但是用户的权限发生变化怎么办?没办法进行实时的控制权限。
2.建议存储到redis中或者mongodb中,利用一个表示 + 用户的id作为key进行存储。user_id:[权限1,权限2,]
3.当进行对角色权限增删改查时,需要清空redis或者mongodb中对应拥有用户的存储的权限 键值对
4.当每次登录,或者访问时,需要在api接口或者视图中,判断一下redis中是否存在权限,如果没有,从mysql中取权限存到redis中
3.当每次请求时[白名单除外(登录url/首页url/..)],从redis或者session中进行判断当前是否存在访问的权限,如果没有返回'报错信息' 如果有 就接着执行下面的中间
权限开发需要功能:
1.登录[获取权限,存储到session中 or redis or mongodb]
2.设置一个访问中间件[每次访问,都会将访问的url与权限列表(从存储的地方进行获取)中得url进行对比]
3.如果通过,接着执行并返回请求数据。没有通过返回 中间件直接返回无权内容[django中间件特性,如果存在返回值就会直接返回内容,不会再进行执行]

快速开发流程

1.登录页面是否有权限(有)
2.POST请求,校验用户登录是否合法
3.获取登录当前用户的相关信息,存储到session中
4.用户在此访问服务器发起请求: http://xxx.xx.xx/index 后端编写中间件对用户进行访问权限的判断[当前访问的url是否存在权限列表中]

1.用户登录权限获取逻辑

from django.shortcuts import HttpResponse, render, redirect
from rbac.models import User # 导入用户表
def login(request):
......
1.根据用户获取用户全部角色
获取用户全部的角色对象
user_obj.role.all()
2.获取用户权限角色下的全部url
user_obj.role.filter(permissions__isnull=False).values('permissions__url').distinct()
permissions_list = user_obj.role.filter(permissions__isnull=False).values('permissions__url').distinct()
# 当前用户的获取权限列表
permissions_url_list = [ i.get('permissions__url') for i in permissions_list]
print(permissions_url_list)
.....
注意:
1.在获取角色下的url权限是,需要进行去重
原因:'客户经理'可以有 index/权限 '部门经理'也可以 index/权限,如果角色同时拥有这两个角色,权限重复。
# django orm 语句
user_obj.role.filter(permissions__isnull=False).values('permissions__url').distinct()
# sql 语句
select DISTINCT rbac_permissions.url from rbac_user
INNER JOIN User_Role on rbac_user.id = User_Role.user_id
INNER JOIN rbac_role on rbac_role.id = User_Role.role_id
inner join Role_Permissions on rbac_role.id = Role_Permissions.role_id
INNER JOIN rbac_permissions on Role_Permissions.permissions_id = rbac_permissions.id where rbac_user.id = 1
2.因为权限只是一些url,假设创建的角色关联的权限为空,那么需要进行去空操作
role角色 与 permissions权限 关联关系不能为空,必须有值存在(避免角色关联权限不能为空)
# django orm语句
user_obj.role.filter(permissions__isnull=False).values('permissions__url').distinct()
# sql语句使用内联查询[INNER JOIN],只有满足条件的才会返回(自动将null排除) 如果使用左右查询会出现角色权限为null需要排除null
select url from (select DISTINCT rbac_permissions.url from rbac_user
INNER JOIN User_Role on rbac_user.id = User_Role.user_id
INNER JOIN rbac_role on rbac_role.id = User_Role.role_id
inner join Role_Permissions on rbac_role.id = Role_Permissions.role_id
INNER JOIN rbac_permissions on Role_Permissions.permissions_id = rbac_permissions.id where rbac_user.id = 1) as permissions_url WHERE permissions_url.url not NULL

2.项目中间件逻辑部分

当用户请求时,进行触发
1.获取当前用户请求的url地址
2.获取当前用户在session/reids中保存的权限列表
3.匹配权限信息
4.面对大家够可以访问的url权限 需要设置一个白名单进行处理
1.中间件代码部分:
import re
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin # 导入继承django中间件
# 创建中间件类
class RbacMiddlewareMixin(MiddlewareMixin):
'''
权限中间件认证
process_request请求来时是进行执行
django process_request 特点:存在返回值不在执行其他的中间件与视图,直接返回结果页面(默认返回none)
'''
def process_request(self, request):
# 白名单,如果有部分的url是需要匹配的通过权限比如admin,可以通过正则匹配的方式
valid_url_list = [
'login/',
'admin/.*'
]
# 当前用户的url 获取的是/customer/list/ 进行替换为 customer/list/方便对比
current_url = request.path_info.replace('/','',1)
1.通过正则的方式循环白名单,过滤掉不需要拦截的url
for i in valid_url_list:
if re.match(i,current_url):
return None
2.从session中获取权限列表
permissions_url_list = request.session.get('permissions_url')
if not permissions_url_list:
return JsonResponse({"code":403,'error':'当前用户未登录,请进行登录后再访问'})
# 通过变量控制是否有权限
flag = False
3.循环权限列表,使用正则匹配确认当前的权限是否通过
for url in permissions_url_list:
为什么使用正则,正则匹配的更为准确,并且因为url中存在正则的url。
比如: customer/edit/(?P<cid>\d+)/ 使用常规的匹配方式 customer/edit/1 判断无法通过
正则匹配 re.match('customer/edit/(?P<cid>\d+)/','customer/edit/123456/')
问题:但是使用正则匹配问题:匹配的内容没有终止起始
比如: re.match('customer/edit/','customer/edit/123456/') 也开始可以进行匹配到的
解决: 加上正则终止和起始符号
mathc_url = f"^{url}$"
if re.match(mathc_url,current_url):
flag = True # 有权限赋值为True,如果是False那么就没有权限
if not flag:
# 未通过无权限
return JsonResponse({"code":403,'error':'当前用户没有权限,请让管理员添加权限!'})
2.在配置中将中间件进行注册
MIDDLEWARE = [
'......process_request'
]

3.中间件逻辑部分-优化

1.将使用的变量放到配置文件夹中
config.py
permissions_key = 'permissions_url' # session key
valid_url_list = [
'login/',
'admin/.*'
] # 白名单,如果有部分的url是需要匹配的通过权限比如admin,可以通过正则匹配的方式
2.将登录功能对权限的初始化分解(登录 与 权限初始化)
登录功能只需要导入当前函数进行初始化即可
permission_init.py
from rbac.config import permissions_key
def init_permission(user_obj,request):
'''
在登录时导入,进行权限的初始化(当前用户权限记录)
user_obj: 用户对象,通过orm 获取用户的权限列表
request: 请求对象,将权限存放在session中
'''
# 根据用户获取用户全部权限
# 获取用户全部的角色对象
# print(user_obj.role.all())
# 1.获取当前用户下的全部的权限url(连续跨表查询) 同时去重[因为不同的角色权限会有相同:比如都有index/ hotm/] .distinct()
# 2.因为权限只是一些url,假设创建的角色关联的权限为空,那么需要进行去空操作,permissions__isnull=False,role角色 与 permissions权限 关联关系不能为空,必须有值存在(避免角色关联权限不能为空)
# print(user_obj.role.filter(permissions__isnull=False).values('permissions__url').distinct())
permissions_list = user_obj.role.filter(permissions__isnull=False).values('permissions__url').distinct()
permissions_url_list = [ i.get('permissions__url') for i in permissions_list]
request.session[permissions_key] = permissions_url_list
3.中间件进行微调
import re
from django.http import JsonResponse
from django.utils.deprecation import MiddlewareMixin # 导入继承django中间件
from rbac.config import permissions_key,valid_url_list # 权限配置文件中 key 白名单
# 创建中间件类
class RbacMiddlewareMixin(MiddlewareMixin):
'''权限中间件认证'''
def process_request(self, request):
'''
当用户请求时,进行触发
1.获取当前用户请求的url地址
2.获取当前用户在session中保存的权限列表
3.匹配权限信息
'''
# 当前用户的url 获取的是/customer/list/ 进行替换为 customer/list/方便对比
current_url = request.path_info.replace('/','',1)
for i in valid_url_list:
if re.match(i,current_url):
return None
permissions_url_list = request.session.get(permissions_key)
if not permissions_url_list:
return JsonResponse({"code":403,'error':'当前用户未登录,请进行登录后再访问'})
flag = False # 通过变量控制是否有权限
# 因为在url中存在 customer/edit/(?P<cid>\d+)/ 正则的url需要使用正则进行匹配
# customer/edit/1 正则匹配 customer/edit/(?P<cid>\d+)/
for url in permissions_url_list:
# 通过有有权限
# 但是使用正则匹配问题:匹配的内容没有终止起始
# 比如: re.match('customer/edit/','customer/edit/123456/') 也开始可以进行匹配到的
# 解决: 加上正则终止和起始符号
mathc_url = f"^{url}$"
if re.match(mathc_url,current_url):
flag = True # 有权限赋值为True,如果是False那么就没有权限
if not flag:
# 未通过无权限
return JsonResponse({"code":403,'error':'当前用户没有权限,请让管理员添加权限!'})

权限之菜单

一级菜单设计思路/代码

权限表与菜单的关系 (包含关系)
1.需要在权限表中设置一个字段,用来表示 当前权限是否可以作为功能
2.在admin中进行配置当前字段
3.在权限出初始化函数中将,可以作为菜单的权限存储到列表保存到session中
1.表结构
from django.db import models
__all__ = ['Permissions', 'Role', 'User']
class Permissions(models.Model):
class Mate:
db_table = 'Permissions'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='标题')
url = models.CharField(max_length=255, verbose_name='正则模式下的url')
# 添加当前字段 当前字段为True那么就是可以当做菜单
menu = models.BooleanField(blank=True,null=True,verbose_name='当前权限是否可以作为菜单')
def __str__(self):
return self.title
class Role(models.Model):
class Mate:
db_table = 'Role'
id = models.AutoField(primary_key=True)
role_name = models.CharField(max_length=255, verbose_name='角色名称')
permissions = models.ManyToManyField(to='Permissions', db_table='Role_Permissions', verbose_name='对多对权限角色报绑定字段',blank=True)
def __str__(self):
return self.role_name
class User(models.Model):
class Mate:
db_table = 'User'
id = models.AutoField(primary_key=True)
user_name = models.CharField(max_length=128, verbose_name='账户')
password = models.CharField(max_length=128, verbose_name='密码')
user_email = models.CharField(max_length=128, verbose_name='邮箱')
role = models.ManyToManyField(to='Role', db_table='User_Role', verbose_name='多对多用户与角色绑定字段')
def __str__(self):
return self.user_name
2.在admin中进行设置对权限进行设置,那些权限可以作为菜单
3.在权限初始化函数中进行初始化菜单设置
from rbac.config import permissions_key, menu_list
def init_permission(user_obj, request):
'''
在登录时导入,进行权限的初始化(当前用户权限记录)
user_obj: 用户对象,通过orm 获取用户的权限列表
request: 请求对象,将权限存放在session中
'''
# 1.权限url 2.权限名称 3.权限可以作为菜单
permissions_list = user_obj.role.filter(permissions__isnull=False).values('permissions__url',
'permissions__menu',
'permissions__title').distinct()
# 当前用户的权限列表
permissions_url_list = [i.get('permissions__url') for i in permissions_list]
# 当前用户的菜单列表(可以作为菜单的列表) 获取url与title
menu_url_list = [{'menu_url': i.get('permissions__url'), 'menu_title': i.get('permissions__title')} for i in
permissions_list if i.get('permissions__menu')]
print(menu_url_list)
request.session[menu_list] = menu_url_list
request.session[permissions_key] = permissions_url_list

仅限前后端不分离使用

在用户登录后,需要将菜单信息给到用户页面中 进行展示
在html页面进行循环展示
<div class="static-menu">
{% for menu in request.session.menu_list %}
<a href="/{{ menu.menu_url }}" class="active">{{ menu.menu_title }}</a>
{% endfor %}
</div>
这种方式不行,因为需要手动的配置,可以设置一个变量使用的是django inclusion_tag
1.创建一个文件 rbac/templatetags/menu.py
import os
from django.template import Library
path = os.path.abspath(os.path.dirname(__file__))
register = Library()
# inclusion_tag(传入模板的路径)
@register.inclusion_tag(os.path.join(path, '../templates', 'static_menu.html'))
def static_menu(request):
'''
1菜单
request 使用当前inclusion_tag方法传入的位置参数
'''
menu_list = request.session['menu_list']
for i in menu_list:
if i.get('menu_url') == request.path.replace('/','',1): # 如果和当前访问的url一致那么设置默认选中
i['class'] = 'active'
return {'menu_list':menu_list} # 当前传入的值就会被inclusion_tag(传入模板的路径)接受
2.创建一个模板 rbac/templates/static_menu.html
接受static_menu方法返回的结果变量
<div class="static-menu">
{% for menu in menu_list %}
<a href="/{{ menu.menu_url }}" class={{ menu.class }} >{{ menu.menu_title }}</a>
{% endfor %}
</div>
3.使用当前方法的模板
只要其他模板使用就会将 inclusion_tag(传入模板的路径) 将路径的模板进行渲染到使用者的页面中
{% load menu %} # 找到当前的py文件
# 在需要的位置进行引用当前文件下的inclusion_tag方法同时传入一个值request
{% static_menu request %}
1.使用inclusion_tag(模板路径)装饰一个函数
2.装饰的函数的返回值会给到模板路径中得模板作为参数使用
3.使用者需要在使用的模板进行 load 当前 inclusion_tag 使用的函数文件名
4.在需要使用的位置将函数名进行 {% 函数名 参数1 参数2%}

二级菜单设计思路/代码

1.1级菜单没有必要进行跳转,只是为了展开二级菜单(可以写死)
2.2级菜单需要进行跳转,需要url,需要进行跳转
菜单的数据结构:
[
{
title:'1级菜单名称',
children:[
{title:'2级菜单1'},
{title:'2级菜单2'},
]
}
]
可以进行双层循环展示
思路
1.表结构修改
2.session中存储的菜单信息结构进行变化
3.修改权限初始化的菜单内容部分的信息
4.需要修改页面显示效果

表结构修改

from django.db import models
__all__ = ['Permissions', 'Role', 'User', 'Menu']
class Menu(models.Model):
'''存放1级菜单'''
class Mate:
db_table = 'Menu'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='1级菜单名称')
def __str__(self):
return self.title
class Permissions(models.Model):
'''权限表'''
class Mate:
db_table = 'Permissions'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='标题')
url = models.CharField(max_length=255, verbose_name='正则模式下的url')
# 创建归属关系,如果menu存在值 那么就是Menu表所属的2级菜单 null那么就不是菜单
menu = models.ForeignKey(blank=True, null=True, verbose_name='所属菜单', to='Menu', on_delete=models.CASCADE)
def __str__(self):
return self.title
class Role(models.Model):
'''角色表'''
class Mate:
db_table = 'Role'
id = models.AutoField(primary_key=True)
role_name = models.CharField(max_length=255, verbose_name='角色名称')
permissions = models.ManyToManyField(to='Permissions', db_table='Role_Permissions', verbose_name='对多对权限角色报绑定字段',
blank=True)
def __str__(self):
return self.role_name
class User(models.Model):
'''用户表'''
class Mate:
db_table = 'User'
id = models.AutoField(primary_key=True)
user_name = models.CharField(max_length=128, verbose_name='账户')
password = models.CharField(max_length=128, verbose_name='密码')
user_email = models.CharField(max_length=128, verbose_name='邮箱')
role = models.ManyToManyField(to='Role', db_table='User_Role', verbose_name='多对多用户与角色绑定字段')
def __str__(self):
return self.user_name

用户登录权限初始化函数

from rbac.config import permissions_key, menu_session_name
def init_permission(user_obj, request):
'''
在登录时导入,进行权限的初始化(当前用户权限记录)
user_obj: 用户对象,通过orm 获取用户的权限列表
request: 请求对象,将权限存放在session中
'''
# 权限url 权限名称 权限可以作为菜单
permissions_list = user_obj.role.filter(permissions__isnull=False).values(
'permissions__url', # 用户关联角色关联权限(全部权限url)
# 用户关联角色关联权限(全部权限url名称)
'permissions__title',
# 用户关联角色关联权限(全部权限url 1级菜单id)
'permissions__menu__id',
# 用户关联角色关联权限(全部权限url 1级菜单的名称)
'permissions__menu__title'
).distinct()
# 1.当前用户的权限列表
permissions_url_list = [i.get('permissions__url') for i in permissions_list]
# 2.获取当前用户的菜单信息 ( 1级菜单 与 二级菜单 ) 变量构建
menu_dict = {}
for item in permissions_list:
# 1.children : 二级菜单全部参数
children_menu = {'permissions_url': item.get('permissions__url'),
"permissions_title": item.get('permissions__title')}
# 2.当前判断是为了构造结构 将1菜单下的全部二级菜单归属到children 1级菜单的列表中
# 3.1级菜单的id 与 title
one_menu_id = item.get('permissions__menu__id')
# 4.菜单结构生成与条件过滤
if not one_menu_id: # 如果没有menu_id 直接跳过(不能成为2级菜单)
continue
if one_menu_id in menu_dict: # 如果当前的menu_id 存在构建的结构中 就将二级菜单进行append添加children中
menu_dict.get(one_menu_id).get('children').append(children_menu)
else: # 如果当前的menu_id 不在 menu_dict 结构中就进行构建
menu_dict[one_menu_id] = {
"permissions__menu__title": item.get('permissions__menu__title'),
'children': [children_menu, ]
}
request.session[menu_session_name] = menu_dict
request.session[permissions_key] = permissions_url_list
menu_dict变量构建:
{
1: {
'permissions__menu__title': '客户管理',
'children': [
{'permissions_url': 'customer/list/', 'permissions_title': '客户列表'},
{'permissions_url': 'payment/list/', 'permissions_title': '付款列表'}
]
},
2: {
'permissions__menu__title': '账单管理',
'children': [
{'permissions_url': 'customer/list/', 'permissions_title': '客户列表'},
{'permissions_url': 'payment/list/', 'permissions_title': '付款列表'}
]
}
....
....
....
}

显示2级菜单(不分离有用)

@register.inclusion_tag(os.path.join(path, '../templates', 'multi_menu.html'))
def menu_menu(request):
'''
2菜单模板
'''
menu_list = request.session[menu_session_name]
for item in menu_list.values():
for children in item.get('children'): # 如果当前访问的url是二级菜单的url,给它默认样式,同时让1级菜单展开
if children.get('permissions_url') == request.path.replace('/', '', 1):
children['class'] = 'active'
item['body_hidden'] = ''
else: # 如果访问的不是就进行闭合
item['body_hidden'] = 'hidden'
return {'menu_list': menu_list}
<div class="multi-menu">
{% for menu in menu_list.values %}
<div class="item">
<div class="title">
{{ menu.permissions__menu__title }}
</div>
<div class="body {{ menu.body_hidden }}">
{% for children in menu.children %}
<a href="/{{ children.permissions_url }}"
class="{{ children.class }}">{{ children.permissions_title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>

不能作为二级菜单的权限与二级菜单做归属关系(不分离有用)

还有好多方式能解决当前的问题
当点击非2级菜单是,归属的二级菜单直接进行默认选中效果
1.在登录是重置权初始化
权限的结构要变为:
permissions_key = [
{id:1,url:'xxxx/xx/',pid:null}, # 可以作为菜单的权限
{id:2,url:'xxxx/xx/',pid:1}, # 不可以作为菜单的权限 归属于pid 1
{id:3,url:'xxxx/xx/',pid:1}, # 不可以作为菜单的权限 归属于pid 1
]
菜单的结构变为:
menu_session_name = {
1: {
'permissions__menu__title': '客户管理',
'children': [
{id:1,'permissions_url': 'customer/list/', 'permissions_title': '客户列表',pid:null},
{id:2,'permissions_url': 'payment/list/', 'permissions_title': '付款列表',pid:null}
]
},
}
2.当用户登录后,再次访问通过中间件时(跳转到首页时的操作)
for i in permissions_key:
# 设置一个参数存储
当前访问的url归属于那个二级菜单的id = item['pid'] or item['id']
3.在菜单显示的inclusion_tag的部分,修改判断的条件,变为根据id判断
for item in menu_list.values():
for children in item.get('children'): # 如果当前访问的url是二级菜单的url,给它默认样式,同时让1级菜单展开
if children.get('id') == 当前访问的url归属于那个二级菜单的id:
children['class'] = 'active'
item['body_hidden'] = ''
else: # 如果访问的不是就进行闭合
item['body_hidden'] = 'hidden'
return {'menu_list': menu_list}

表结构

from django.db import models
__all__ = ['Permissions', 'Role', 'User', 'Menu']
class Menu(models.Model):
'''存放1级菜单'''
class Mate:
db_table = 'Menu'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='1级菜单名称')
def __str__(self):
return self.title
class Permissions(models.Model):
class Mate:
db_table = 'Permissions'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='标题')
url = models.CharField(max_length=255, verbose_name='正则模式下的url')
# 创建归属关系,如果menu存在值 那么就是Menu表所属的2级菜单 null那么就不是菜单
menu = models.ForeignKey(blank=True, null=True, verbose_name='所属菜单', to='Menu', on_delete=models.CASCADE)
# 创建自关联,将不能成为2级菜单的权限关联到,可以成为2级菜单的归属
pid = models.ForeignKey(to='Permissions', verbose_name='不能成为2级菜单的权限关联到可以成为2级菜单下面 归属关系', related_name='parents',on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return self.title
class Role(models.Model):
class Mate:
db_table = 'Role'
id = models.AutoField(primary_key=True)
role_name = models.CharField(max_length=255, verbose_name='角色名称')
permissions = models.ManyToManyField(to='Permissions', db_table='Role_Permissions', verbose_name='对多对权限角色报绑定字段',
blank=True)
def __str__(self):
return self.role_name
class User(models.Model):
class Mate:
db_table = 'User'
id = models.AutoField(primary_key=True)
user_name = models.CharField(max_length=128, verbose_name='账户')
password = models.CharField(max_length=128, verbose_name='密码')
user_email = models.CharField(max_length=128, verbose_name='邮箱')
role = models.ManyToManyField(to='Role', db_table='User_Role', verbose_name='多对多用户与角色绑定字段')
def __str__(self):
return self.user_name

用户登录权限初始化部分

from rbac.config import permissions_key, menu_session_name
def init_permission(user_obj, request):
'''
在登录时导入函数,进行权限的初始化(当前用户权限记录)
user_obj: 用户对象,通过orm 获取用户的权限列表
request: 请求对象,将权限存放在session中
'''
# 权限url 权限名称 权限可以作为菜单
permissions_list = user_obj.role.filter(permissions__isnull=False).values(
# 权限id
"permissions__id",
# 用户关联角色关联权限(全部权限url)
'permissions__url',
# 用户关联角色关联权限(全部权限url名称)
'permissions__title',
# 用户关联角色关联权限(全部权限url 1级菜单id)
'permissions__menu__id',
# 用户关联角色关联权限(全部权限url 1级菜单的名称)
'permissions__menu__title',
# 获取pid,非菜单权限的的归属菜单的id(自关联)
"permissions__pid__id"
).distinct()
# 1.当前用户的权限列表
permissions_url_list = []
# 2.获取当前用户的菜单信息 ( 1级菜单 与 二级菜单 ) 结构变量
menu_dict = {}
for item in permissions_list:
1.权限的全部有用的参数组成一个字典
children_menu = {
"id": item.get('permissions__id'),
'permissions_url': item.get('permissions__url'),
"permissions_title": item.get('permissions__title'),
"pid":item.get('permissions__pid__id')
}
2. 将权限添加到权限列表中
permissions_url_list.append(children_menu)
3.构建1级菜单与2级菜单的数据结构
one_menu_id = item.get('permissions__menu__id')
'''
-- 具体逻辑
3.1.如果没有menu_id 直接跳过(不能成为2级菜单)
3.2.如果当前的menu_id 存在构建的结构中 就将二级菜单进行append添加children中
3.3.如果当前的menu_id 不在 menu_dict 结构中就进行构建
'''
if not one_menu_id:
continue
if one_menu_id in menu_dict:
menu_dict.get(one_menu_id).get('children').append(children_menu)
else:
menu_dict[one_menu_id] = {
"permissions_menu_title": item.get('permissions__menu__title'),
'children': [children_menu, ]
}
request.session[menu_session_name] = menu_dict
request.session[permissions_key] = permissions_url_list

中间件修改为

from django.utils.deprecation import MiddlewareMixin # 导入继承django中间件
import re
from django.http import JsonResponse
from rbac.config import permissions_key, valid_url_list # 权限key 白名单
# 创建中间件类
class RbacMiddlewareMixin(MiddlewareMixin):
'''权限中间件认证功能(django 中间件)'''
def process_request(self, request):
'''
当用户请求时,进行触发
1.获取当前用户请求的url地址
2.获取当前用户在session中保存的权限列表
3.匹配权限信息
'''
1.当前用户的url 获取的是/customer/list/ 进行替换为 customer/list/方便对比
current_url = request.path_info.replace('/', '', 1)
for i in valid_url_list:
if re.match(i, current_url):
return None
permissions_url_list = request.session.get(permissions_key)
if not permissions_url_list:
return JsonResponse({"code": 403, 'error': '当前用户未登录,请进行登录后再访问'})
2.通过变量控制是否有权限
flag = False
3.循环从session获取权限信息
for url in permissions_url_list:
'''
3.1.因为在url中存在 customer/edit/(?P<cid>\d+)/ 正则的url需要使用正则进行匹配
例如:customer/edit/1 正则匹配 customer/edit/(?P<cid>\d+)/
3.2.通过有有权限 但是使用正则匹配问题:匹配的内容没有终止起始
例如: re.match('customer/edit/','customer/edit/123456/') 也开始可以进行匹配到的
3.3.解决: 加上正则终止和起始符号
3.4. 有权flag限赋值为True,如果是False那么就没有权限
'''
re_url = f"^{url.get('permissions_url')}$"
if re.match(re_url, current_url):
# 新添加 menu_id 变量 当访问非菜单权限时,默认选中归属的二级菜单选中
request.menu_id = url.get('pid') or url.get('id')
flag = True
if not flag:
# 未通过无权限
return JsonResponse({"code": 403, 'error': '当前用户没有权限,请让管理员添加权限!'})

二级菜单inclusion_tag修改

@register.inclusion_tag(os.path.join(path, '../templates', 'multi_menu.html'))
def menu_menu(request):
'''
2菜单模板
'''
menu_list = request.session[menu_session_name]
for item in menu_list.values():
for children in item.get('children'): # 如果当前访问的url是二级菜单的url,给它默认样式,同时让1级菜单展开
# 判断不在使用url进行判断,而使用id进行判断
# menu_id:如果是非菜单权限 就是关联的所属二级权限绑定pid是存在值
# menu_id: 如果是菜单权限权限 那么就是自己的id因为 pid为null
# menu_id = url.get('pid') or url.get('id')
if children.get('id') == request.menu_id:
children['class'] = 'active'
item['body_hidden'] = ''
else: # 如果访问的不是就进行闭合
item['body_hidden'] = 'hidden'
print(request.path.replace('/', '', 1))
return {'menu_list': menu_list}

multi_menu模板

<div class="multi-menu">
{% for menu in menu_list.values %}
<div class="item">
<div class="title">
{{ menu.permissions_menu_title }}
</div>
<div class="body {{ menu.body_hidden }}">
{% for children in menu.children %}
<a href="/{{ children.permissions_url }}"
class="{{ children.class }}">{{ children.permissions_title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}
</div>

权限之导航路由

当进行访问时,需要修改导航的路由内容
因为在二级菜单中得pid已经作何了这层关系,只需要进行添加判断语句

用户登录权限初始化

from rbac.config import permissions_key, menu_session_name
def init_permission(user_obj, request):
'''
在登录时导入函数,进行权限的初始化(当前用户权限记录)
user_obj: 用户对象,通过orm 获取用户的权限列表
request: 请求对象,将权限存放在session中
'''
# 权限url 权限名称 权限可以作为菜单
permissions_list = user_obj.role.filter(permissions__isnull=False).values(
"permissions__id",
'permissions__url',
'permissions__title',
'permissions__menu__id',
'permissions__menu__title',
"permissions__pid__id",
# 非菜单权限的的归属菜单的id(自关联) title 新增
"permissions__pid__title",
# 非菜单权限的的归属菜单的id(自关联) url 新增
"permissions__pid__url",
).distinct()
# 1.当前用户的权限列表
permissions_url_list = []
# 2.获取当前用户的菜单信息 ( 1级菜单 与 二级菜单 ) 结构变量
menu_dict = {}
for item in permissions_list:
1.权限的全部有用的参数组成一个字典
children_menu = {
"id": item.get('permissions__id'),
'permissions_url': item.get('permissions__url'),
"permissions_title": item.get('permissions__title'),
"pid": item.get('permissions__pid__id'),
# 多加的两个键值对: 如果访问的是非菜单,需要将他的归属的2级菜单也同时显示 新增
"ptitle": item.get('permissions__pid__title'),
"purl": item.get('permissions__pid__url')
}
2. 将权限添加到权限列表中
permissions_url_list.append(children_menu)
3.构建1级菜单与2级菜单的数据结构
one_menu_id = item.get('permissions__menu__id')
'''
-- 具体逻辑
3.1.如果没有menu_id 直接跳过(不能成为2级菜单)
3.2.如果当前的menu_id 存在构建的结构中 就将二级菜单进行append添加children中
3.3.如果当前的menu_id 不在 menu_dict 结构中就进行构建
'''
if not one_menu_id:
continue
if one_menu_id in menu_dict:
menu_dict.get(one_menu_id).get('children').append(children_menu)
else:
menu_dict[one_menu_id] = {
"permissions_menu_title": item.get('permissions__menu__title'),
'children': [children_menu, ]
}
request.session[menu_session_name] = menu_dict
request.session[permissions_key] = permissions_url_list

中间件修改为

from django.utils.deprecation import MiddlewareMixin # 导入继承django中间件
import re
from django.http import JsonResponse
from rbac.config import permissions_key, valid_url_list # 权限key 白名单
# 创建中间件类
class RbacMiddlewareMixin(MiddlewareMixin):
'''权限中间件认证功能(django 中间件)'''
def process_request(self, request):
'''
当用户请求时,进行触发
1.获取当前用户请求的url地址
2.获取当前用户在session中保存的权限列表
3.匹配权限信息
'''
1.当前用户的url 获取的是/customer/list/ 进行替换为 customer/list/方便对比
current_url = request.path_info.replace('/', '', 1)
for i in valid_url_list:
if re.match(i, current_url):
return None
permissions_url_list = request.session.get(permissions_key)
if not permissions_url_list:
return JsonResponse({"code": 403, 'error': '当前用户未登录,请进行登录后再访问'})
2.通过变量控制是否有权限
flag = False
# 导航显示列表 新增
navigation_list = [
{"title": "首页", "url": "#"}
]
3.循环从session获取权限信息
for url in permissions_url_list:
'''
3.1.因为在url中存在 customer/edit/(?P<cid>\d+)/ 正则的url需要使用正则进行匹配
例如:customer/edit/1 正则匹配 customer/edit/(?P<cid>\d+)/
3.2.通过有有权限 但是使用正则匹配问题:匹配的内容没有终止起始
例如: re.match('customer/edit/','customer/edit/123456/') 也开始可以进行匹配到的
3.3.解决: 加上正则终止和起始符号
3.4. 有权flag限赋值为True,如果是False那么就没有权限
'''
re_url = f"^{url.get('permissions_url')}$"
if re.match(re_url, current_url):
# menu_id 变量 当访问非菜单权限时,默认选中归属的二级菜单选中
request.menu_id = url.get('pid') or url.get('id')
flag = True
if url.get('pid'): # 新增
# 存在pid,那么就说明当前的url不属于菜单权限,需要加入两个
# 1.所属的2级菜单权限 url 与 title
# 2.自己的url 与title
navigation_list.extend(
[{"title": url.get('ptitle'), "url": url.get('purl')},
{"title": url.get('permissions_title'), "url": url.get('permissions_url')}]
)
else:
# 那么就是访问的属于菜单的权限,
# 就显示自己的url 与 title
navigation_list.extend([
{"title": url.get('permissions_title'), "url": url.get('permissions_url')}
])
request.navigation_list = navigation_list
if not flag:
# 未通过无权限
return JsonResponse({"code": 403, 'error': '当前用户没有权限,请让管理员添加权限!'})

添加模板

@register.inclusion_tag(os.path.join(path, '../templates', 'navigation.html'))
def navigation(request):
'''
导航栏模板
'''
navigation_list = request.navigation_list
return {'navigation_list': navigation_list}
navigation.html
{% for navigation in navigation_list %}
<li><a href="/{{ navigation.url }}">{{ navigation.title }}</a></li>
{% endfor %}
# 在其他模板中使用
{% load menu %}
{% navigation request %}

权限的控制力度到按钮级别

也就是在页面按钮(每一个按钮都是一个发送数据到后端,相当于一个url)都属于权限,也就是将当前这些按钮进行控制。如果没有权限的按钮就进行隐藏或者变为不可点击,如果有权限就进行显示可点击
在前后端不分离处理时模板处理,
根据模板语法进行判断当前是否存在权限
{% if user/add in 权限列表 %}
<a href="user/add">添加用户</a>
{%endfor%}
因为这种方式太过于繁琐,可以在数据库中添加一个字段 为权限起一个别名的字段

数据库修改

from django.db import models
__all__ = ['Permissions', 'Role', 'User', 'Menu']
class Menu(models.Model):
'''存放1级菜单'''
class Mate:
db_table = 'Menu'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='1级菜单名称')
def __str__(self):
return self.title
class Permissions(models.Model):
class Mate:
db_table = 'Permissions'
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=128, verbose_name='标题')
url = models.CharField(max_length=255, verbose_name='正则模式下的url')
# 为权限设置一个别名 新增
name = models.CharField(max_length=255,verbose_name='权限的别名,用户控制前后端不分离模板的按钮可显示否',null=False,unique=True)
# 创建归属关系,如果menu存在值 那么就是Menu表所属的2级菜单 null那么就不是菜单
menu = models.ForeignKey(blank=True, null=True, verbose_name='所属菜单', to='Menu', on_delete=models.CASCADE)
# 创建自关联,将不能成为2级菜单的权限关联到,可以成为2级菜单的归属
pid = models.ForeignKey(to='Permissions', verbose_name='不能成为2级菜单的权限关联到可以成为2级菜单下面 归属关系', related_name='parents',
on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return self.title
class Role(models.Model):
class Mate:
db_table = 'Role'
id = models.AutoField(primary_key=True)
role_name = models.CharField(max_length=255, verbose_name='角色名称')
permissions = models.ManyToManyField(to='Permissions', db_table='Role_Permissions', verbose_name='对多对权限角色报绑定字段',
blank=True)
def __str__(self):
return self.role_name
class User(models.Model):
class Mate:
db_table = 'User'
id = models.AutoField(primary_key=True)
user_name = models.CharField(max_length=128, verbose_name='账户')
password = models.CharField(max_length=128, verbose_name='密码')
user_email = models.CharField(max_length=128, verbose_name='邮箱')
role = models.ManyToManyField(to='Role', db_table='User_Role', verbose_name='多对多用户与角色绑定字段')
def __str__(self):
return self.user_name

用户登录权限初始化修改

# 将 权限列表改为权限字典结构进行变化
from rbac.config import permissions_key, menu_session_name
def init_permission(user_obj, request):
'''
在登录时导入函数,进行权限的初始化(当前用户权限记录)
user_obj: 用户对象,通过orm 获取用户的权限列表
request: 请求对象,将权限存放在session中
'''
# 权限url 权限名称 权限可以作为菜单
permissions_list = user_obj.role.filter(permissions__isnull=False).values(
"permissions__id",
'permissions__url',
'permissions__title',
'permissions__menu__id',
'permissions__menu__title',
"permissions__pid__id",
"permissions__pid__title",
"permissions__pid__url",
# 新增将权限别名获取
"permissions__name"
).distinct()
# 1.当前用户的权限列表
permissions_dict = {} # 从列表变为字典
# 2.获取当前用户的菜单信息 ( 1级菜单 与 二级菜单 ) 结构变量
menu_dict = {}
for item in permissions_list:
1.权限的全部有用的参数组成一个字典
children_menu = {
"id": item.get('permissions__id'),
'permissions_url': item.get('permissions__url'),
"permissions_title": item.get('permissions__title'),
"pid": item.get('permissions__pid__id'),
# 多加的两个键值对: 如果访问的是非菜单,需要将他的归属的2级菜单也同时显示 新增
"ptitle": item.get('permissions__pid__title'),
"purl": item.get('permissions__pid__url')
}
2. 将权限添加到权限中
permissions_dict[item.get('permissions__name')] = children_menu
3.构建1级菜单与2级菜单的数据结构
one_menu_id = item.get('permissions__menu__id')
'''
-- 具体逻辑
3.1.如果没有menu_id 直接跳过(不能成为2级菜单)
3.2.如果当前的menu_id 存在构建的结构中 就将二级菜单进行append添加children中
3.3.如果当前的menu_id 不在 menu_dict 结构中就进行构建
'''
if not one_menu_id:
continue
if one_menu_id in menu_dict:
menu_dict.get(one_menu_id).get('children').append(children_menu)
else:
menu_dict[one_menu_id] = {
"permissions_menu_title": item.get('permissions__menu__title'),
'children': [children_menu, ]
}
request.session[menu_session_name] = menu_dict
request.session[permissions_key] = permissions_dict

中间件修改

# 以为在权限初始化中进行了结构修改 将列表变为字典
需要进行修改
from django.utils.deprecation import MiddlewareMixin # 导入继承django中间件
import re
from django.http import JsonResponse
from rbac.config import permissions_key, valid_url_list # 权限key 白名单
# 创建中间件类
class RbacMiddlewareMixin(MiddlewareMixin):
'''权限中间件认证功能(django 中间件)'''
def process_request(self, request):
'''
当用户请求时,进行触发
1.获取当前用户请求的url地址
2.获取当前用户在session中保存的权限列表
3.匹配权限信息
'''
current_url = request.path_info.replace('/', '', 1)
for i in valid_url_list:
if re.match(i, current_url):
return None
permissions_dict = request.session.get(permissions_key) # 新增 获取的不在是列表 变为字典
if not permissions_dict:
return JsonResponse({"code": 403, 'error': '当前用户未登录,请进行登录后再访问'})
flag = False
navigation_list = [
{"title": "首页", "url": "#"}
]
for url in permissions_dict.values(): # 获取values()
re_url = f"^{url.get('permissions_url')}$"
if re.match(re_url, current_url):
request.menu_id = url.get('pid') or url.get('id')
if url.get('pid'):
navigation_list.extend(
[{"title": url.get('ptitle'), "url": url.get('purl')},
{"title": url.get('permissions_title'), "url": url.get('permissions_url')}]
)
else:
navigation_list.extend([
{"title": url.get('permissions_title'), "url": url.get('permissions_url')}
])
request.navigation_list = navigation_list
flag = True
if not flag:
return JsonResponse({"code": 403, 'error': '当前用户没有权限,请让管理员添加权限!'})

设置模板方法filter

@register.filter
def has_permissions(request, name):
'''
特点:
1.最多接受两个参数
2.可以作为模板的判断
3.模板使用 需要先lode 在进行使用
4.传参 第一个参数|has_permissions:"第二个参数"
'''
# 因为 session中权限key为字典结构,in的时候是in的字典key
if name in request.session.get(permissions_key):
return True
return False
html中使用
{% load menu %}
# 传入 request 与 权限name名称 通过 has_permissions 查看是否权限存在
{% if request| has_permissions:"payment_edit" %}
<a style="color: #333333;" href="/payment/edit/{{ row.id }}/">
<i class="fa fa-edit" aria-hidden="true"></i>
</a>
{% endif %}

上述总结

1.权限控制(登录后获取权限,再次访问后通过中间件进行判断)
2.动态菜单(在中间件中将动态菜单进行生成)
3.权限的控制到按钮级别(根据权限在html页面判断当前权限是否存在,选择隐藏与显示)

权限功能分配

用户创建 删除 查询 修改
角色创建 修改 查询 删除
权限创建 修改 查询 删除
角色 分配 权限
用户 分配 角色
# 4张表的之间的关系也需要进行展示添加
1.创建url 角色[单表] 增删改查 显示绑定的权限内容
2.创建url 用户[单表] 正删改查 显示绑定的角色内容
3.创建url 菜单[单表] 增删改查
4.创建url 权限[单表] 增删改查 显示自关联[关联表中可以作为二级菜单] 显示绑定1级菜单
最需要注意的:
1.这几张表的之间的关系,
2.如果是前后端不分离的情况下,前端框架的路由与数据库中菜单表的关系,路由与后端的权限表api接口的关系进行绑定,前端框架的循环展示当前用户的菜单以及菜单下的2级菜单(前端框架的路由),点击2级菜单,就会从后端api接口中获取数据。进行保定关系设置。
前后端分离状态下的:https://www.yuque.com/zhanghaofei/blog/xrpz9p权限思路

任务分解

1.角色管理 单表操作 增删改查
1.不分离(modlform)使用
2.反向生成 namespace 和 name 的反向生成url
3.django模板查找顺序 按照注册app的顺序一个一个找
2.用户管理 单表操作 增删改查
1.不分离(modlform)使用
2.反向生成 namespace 和 name 的反向生成url
3.django模板查找顺序 按照注册app的顺序一个一个找
3.菜单表与权限表(菜单与权限管理) 2张表的增删改查
1.需要展示一个1级菜单的添加删除修改编辑
2.在添加修改中需要进行绑定权限表中url进行二级菜单
3.同时二级菜单需要绑定非菜单的权限url

发现项目中权限django

批量的添加权限,发现项目中得权限(url)
前后端不分离:
1.modelform 单个表单的添加操作
2.modelformset 多个表单添加操作验证 批量操作进行使用
前后端分离:
一个api接口将数据库中得已经记录的url与项目中得url进行对比显示在前端即可
通过框架内部的方法获取全部项目下的url进行处理与数据库中进行对比获取剩下的
from collections import OrderedDict # 有序字典
from django.conf import settings # 配置文件
from django.utils.module_loading import import_string # 根据字符串获导入一个模块
from django.urls import URLResolver, URLPattern # 路由的类型
def recursion_urls(pre_namespace, pre_url, urlpatterns, url_ordered_dict):
"""
# url的递归操作
pre_namespace : 路由分发的namespace
per_url:路由分发的路由
urlpatterns:路由关系
url_ordered_dict:用来保存获取的所有的路由
for item in urlpatterns:
item 是每一个url url.name 获取url别名 item.pattern 获取url对象
item.pattern._regex # 获取url
"""
for item in urlpatterns:
if isinstance(item, URLPattern): # 非路由分发,不需要进行循环操作
if not item.name: # 如果没有给url起别名,让当前自己的url作为自己的别名
name = str(item.pattern).replace('^', "").replace('$', '')
elif pre_namespace: # 有路由分发的别名 进行拼接
name = "%s:%s" % (pre_namespace, item.name)
else: # 没有路由分发的别名就使用自己的
name = item.name
url = pre_url + str(item.pattern)
url = url.replace('^', "").replace('$', '') # 清除终止符与其实符
url_ordered_dict[name] = url # 存储到有序字典中
elif isinstance(item, URLResolver): # 是路由分发需要进行接着循环操作,进行递归操作
if pre_namespace: # 如果传入了namespace就需要进行拼接 分发别名使用
if item.namespace: # 如果自己有拼接 namespace
namespace = "%s:%s" % (pre_namespace, item.namespace)
else: # 没有就是用传入的 namespace
namespace = pre_namespace
else: # 如果没有那么使用自己
if item.namespace:
namespace = item.namespace
else: # 自己的没有默认为none
namespace = None
recursion_urls(namespace, pre_url + str(item.pattern), item.url_patterns, url_ordered_dict)
def get_all_url_dict():
'''获取全部的项目的路由(需要有name别名)'''
url_ordered_dict = OrderedDict()
md = import_string(settings.ROOT_URLCONF)
recursion_urls(None, '/', md.urlpatterns, url_ordered_dict)
return url_ordered_dict
# 视图:
def multi_permissions(request):
'''
获取项目中得全部的url
'''
a = get_all_url_dict()
for k,v in a.items():
print(k,v)
return HttpResponse('ok')

总结前后端分离

1.当用户登录后获取权限(用户登录页面白名单)
1.1. 当用户登录初始化数据库中得权限存储到session或者缓存中
2.当用户在进行访问时
2.1 通过中间件 从缓存中获取初始化的权限信息 无权访问返回内容 与通过 返回的内容
权限需要根据项目中url而定
每一个url相当于一个接口
vue --- django
vue 有自己的路由
django 有自己的路由(是接口数据)
数据库表结构:
# 固定不变的
1.权限表 api接口url
2.角色表 角色绑定的权限
3.用户表 用户绑定的角色
4.菜单表
5.vue 前端路由表
菜单表 # 作为在vue前端渲染的循环的 1级菜单信息
nenu_name : 菜单表的名称
vue 前端路由表 # 二级菜单,显示的页面
url: 前端的路由信息
menu_id : 与菜单表绑定的关系
权限表 # 后端接口表
url : 后端的接口
vue 前端路由_id # 归属到前端路由下面
角色表 # 角色表
权限表_id :拥有的权限信息
用户表 #
角色表_id : 拥有那些角色
# 例如:
页面显示
'''
用户管理: 一级菜单(数据库菜单表生成的)
用户列表 二级菜单(前端的路由渲染的)
权限列表 二级菜单(前端的路由渲染的)
角色列表 二级菜单(前端的路由渲染的)
....
'''
数据显示(在前端页面进行循环展示侧边栏菜单)
[
{
'title':'用户管理',"id":"1作为渲染菜单的唯一标识",
"child":[
{'title':'用户列表',"url":'url/list/',},
{'title':'权限列表',"url":'url/list/',},
{'title':'角色列表',"url":'url/list/',},
]
}
]
用户登录后根据角色找到权限信息
权限找到前端页面的显示路由,根据路由找到1级菜单信息
进行渲染操作
前端页面显示要与后端api接口做归属关系

本文作者:_wangkx

本文链接:https://www.cnblogs.com/kaixinblog/p/17878652.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _wangkx  阅读(96)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起