用户权限 (知识点)

时间:2021-09-01 15:29:02

一. 权限用户表

a. SQL表结构

用户权限 (知识点)用户权限 (知识点)
from django.db import models


class User(models.Model):
"""
用户表
"""
username
= models.CharField(verbose_name='用户名', max_length=32)
password
= models.CharField(verbose_name='密码', max_length=64)
email
= models.EmailField(verbose_name='邮箱')

def __str__(self):
return self.username


class Role(models.Model):
"""
角色表
"""
caption
= models.CharField(verbose_name='角色', max_length=32)

def __str__(self):
return self.caption


class User2Role(models.Model):
"""
用户角色关系表
"""
user
= models.ForeignKey(User, verbose_name='用户', related_name='roles')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='users')

def __str__(self):
return '%s-%s' % (self.user.username, self.role.caption,)


class Menu(models.Model):
"""
菜单表
"""
caption
= models.CharField(verbose_name='菜单名称', max_length=32)
parent
= models.ForeignKey('self', verbose_name='父菜单', related_name='p', null=True, blank=True)

def __str__(self):
prev
= ""
parent
= self.parent
while True:
if parent:
prev
= prev + '-' + str(parent.caption)
parent
= parent.parent
else:
break
return '%s-%s' % (prev, self.caption,)


class Permission(models.Model):
"""
权限
"""
caption
= models.CharField(verbose_name='权限', max_length=32)
url
= models.CharField(verbose_name='URL正则', max_length=128)
menu
= models.ForeignKey(Menu, verbose_name='所属菜单', related_name='permissions',null=True,blank=True)

def __str__(self):
return "%s-%s" % (self.caption, self.url,)


class Action(models.Model):
"""
操作:增删改查
"""
caption
= models.CharField(verbose_name='操作标题', max_length=32)
code
= models.CharField(verbose_name='方法', max_length=32)

def __str__(self):
return self.caption


class Permission2Action2Role(models.Model):
"""
权限操作关系表
"""
permission
= models.ForeignKey(Permission, verbose_name='权限URL', related_name='actions')
action
= models.ForeignKey(Action, verbose_name='操作', related_name='permissions')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='p2as')

class Meta:
unique_together
= (
(
'permission', 'action', 'role'),
)

def __str__(self):
return "%s-%s-%s" % (self.permission, self.action, self.role,)



python3 manage.py startapp app02
models.py

用户权限 (知识点)

b. 输出菜单

用户权限 (知识点)用户权限 (知识点)
urlpatterns = [
url(r
'^app02_test.html', views2.app02_test),
]
urls.py
用户权限 (知识点)用户权限 (知识点)
from django.contrib import admin

# Register your models here.
from app02 import models

admin.site.register(models.User)
admin.site.register(models.Role)
admin.site.register(models.User2Role)
admin.site.register(models.Menu)
admin.site.register(models.Permission)
admin.site.register(models.Action)
admin.site.register(models.Permission2Action2Role)
app02/admin.py
用户权限 (知识点)用户权限 (知识点)
def app02_test(request):
"""
需要用户名或用户ID,产出:用户关联所有菜单
:param request:
:return:
"""

# 所有菜单:处理成当前用关联的菜单
all_menu_list = models.Menu.objects.all().values('id', 'caption', 'parent_id')
"""
[
{'id':1, 'caption':'菜单1', parent_id:None},
{'id':2, 'caption':'菜单2', parent_id:None},
{'id':3, 'caption':'菜单3', parent_id:None},
{'id':4, 'caption':'菜单1-1', parent_id:1},
]

{
1:{'id':1, 'caption':'菜单1', parent_id:None,status:False,opened:False,child:[]},
2:{'id':2, 'caption':'菜单2', parent_id:None,status:False,opened:False,child:[]},
3:{'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},
5:{'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},
}
"""
user
= models.User.objects.filter(username='alex').first()
role_list
= models.Role.objects.filter(users__user=user)
permission_list
= models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__id',
'permission__url',
'permission__menu_id',
'permission__caption').distinct()



"""

[
{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 1 },
{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 2 },
{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 3 },
{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 4 },
]
"""
##### 将权限挂靠到菜单上 ########
all_menu_dict = {}
for row in all_menu_list:
row[
'child'] = [] # 添加孩子
row['status'] = False # 是否显示菜单
row['opened'] = False # 是否默认打开
all_menu_dict[row['id']] = row




for per in permission_list:
if not per['permission__menu_id']:
continue

item
= {
'id': per['permission__id'],
'caption': per['permission__caption'],
'parent_id': per['permission__menu_id'],
'url': per['permission__url'],
'status': True,
'opened': False
}

# print(item["url"])
if re.match(per['permission__url'],request.path_info):
# if re.match(per['permission__url'], "/orders.html"):
item['opened'] = True
pid
= item['parent_id']
all_menu_dict[pid][
'child'].append(item)

# 将当前权限前辈status=True
temp = pid # 1.父亲ID
while not all_menu_dict[temp]['status']:
all_menu_dict[temp][
'status'] = True
temp
= all_menu_dict[temp]['parent_id']
if not temp:
break

# 将当前权限前辈opened=True
if item['opened']:
temp1
= pid # 1.父亲ID
while not all_menu_dict[temp1]['opened']:
all_menu_dict[temp1][
'opened'] = True
temp1
= all_menu_dict[temp1]['parent_id']
if not temp1:
break
# ############ 处理菜单和菜单之间的等级关系 ############
"""
all_menu_dict = {
1:{'id':1, 'caption':'菜单1', parent_id:None,status:False,opened:False,child:[{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 1 },]},
2:{'id':2, 'caption':'菜单2', parent_id:None,status:False,opened:False,child:[]},
3:{'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},
5:{'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},
}


all_menu_list= [
{'id':1, 'caption':'菜单1', parent_id:None,status:False,opened:False,child:[{'permission__url':'/order.html','permission__caption': '订单管理','permission__menu_id': 1 }, {'id':4, 'caption':'菜单1-1', parent_id:1,status:False,opened:False,child:[]},]},
{'id':2, 'caption':'菜单2', parent_id:None,status:False,opened:False,child:[]},
{'id':3, 'caption':'菜单3', parent_id:None,status:False,opened:False,child:[]},

]
"""

result
= []
for row in all_menu_list:
pid
= row['parent_id']
if pid:
all_menu_dict[pid][
'child'].append(row)
else:
result.append(row)


##################### 结构化处理结果 #####################
# print(result)
# for row in result:
# # print(row['caption'], row['status'], row['opened'], )
# print(row)

##################### 通过结构化处理结果,生成菜单开始 #####################

def menu_tree(menu_list):
tpl1
= """
<div class='menu-item'>
<div class='menu-header'>{0}</div>
<div class='menu-body {2}'>{1}</div>
</div>
"""
tpl2
= """
<a href='{0}' class='{1}'>{2}</a>
"""

menu_str
= ""
for menu in menu_list:
if not menu['status']:
continue
# menu: 菜单,权限(url)
if menu.get('url'):
# 权限
menu_str += tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])
else:
# 菜单
if menu['child']:
child_html
= menu_tree(menu['child'])
else:
child_html
= ""
menu_str
+= tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')

return menu_str

menu_html
= menu_tree(result)




return render(request, "menu_html.html", {"menu_html":menu_html})
app02/views.py
用户权限 (知识点)用户权限 (知识点)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.menu-body
{
margin-left
: 20px;
}
.menu-body a
{
display
: block;
}
.menu-body a.active
{
color
: red;
}
.hide
{
display
: none;
}
</style>
</head>
<body>

{{ menu_html|safe }}

<script src="/static/jquery-3.2.1.js"></script>
<script>
$(
function () {
$(
".menu-header").click(function () {
$(
this).next().removeClass("hide").parent().siblings().find(".menu-body").addClass("hide")
})
})
</script>
</body>
</html>
menu_html.html

c. result 结果递归 debug调试 

result = 
[
{'opened': True, 'parent_id': None, 'caption': '菜单一', 'id': 1, 'status': True,
'child': [
{'opened': False, 'parent_id': 1, 'caption': '帅哥管理', 'id': 5, 'status': True, 'url': '/shuaige.html'},
{'opened': True, 'parent_id': 1, 'caption': '菜单一 一', 'id': 4, 'status': True,
'child': [
{'opened': True, 'parent_id': 4, 'caption': '用户管理', 'id': 1, 'status': True, 'url': '/users.html'},
{'opened': False, 'parent_id': 4, 'caption': '订单管理', 'id': 2, 'status': True, 'url': '/orders.html'}
]
},
{'opened': False, 'parent_id': 1, 'caption': '菜单一 二', 'id': 5, 'status': False, 'child': []},
{'opened': False, 'parent_id': 1, 'caption': '菜单一 三', 'id': 6, 'status': False, 'child': []}
]
},
{'opened': False, 'parent_id': None, 'caption': '菜单二', 'id': 2, 'status': False, 'child': []},
{'opened': False, 'parent_id': None, 'caption': '菜单三', 'id': 3, 'status': False, 'child': []}
] 
用户权限 (知识点)用户权限 (知识点)
result = [{'opened': True, 'parent_id': None, 'caption': '菜单一', 'id': 1, 'status': True, 'child': [{'opened': False, 'parent_id': 1, 'caption': '帅哥管理', 'id': 5, 'status': True, 'url': '/shuaige.html'}, {'opened': True, 'parent_id': 1, 'caption': '菜单一  一', 'id': 4, 'status': True, 'child': [{'opened': True, 'parent_id': 4, 'caption': '用户管理', 'id': 1, 'status': True, 'url': '/users.html'}, {'opened': False, 'parent_id': 4, 'caption': '订单管理', 'id': 2, 'status': True, 'url': '/orders.html'}]}, {'opened': False, 'parent_id': 1, 'caption': '菜单一 二', 'id': 5, 'status': False, 'child': []}, {'opened': False, 'parent_id': 1, 'caption': '菜单一  三', 'id': 6, 'status': False, 'child': []}]}, {'opened': False, 'parent_id': None, 'caption': '菜单二', 'id': 2, 'status': False, 'child': []}, {'opened': False, 'parent_id': None, 'caption': '菜单三', 'id': 3, 'status': False, 'child': []}]


def menu_tree(menu_list):
tpl1
= """
<div class='menu-item'>
<div class='menu-header'>{0}</div>
<div class='menu-body {2}'>{1}</div>
</div>
"""
tpl2
= """
<a href='{0}' class='{1}'>{2}</a>
"""

menu_str
= ""
for menu in menu_list:
if not menu['status']:
continue
# menu: 菜单,权限(url)
if menu.get('url'):
# 权限
menu_str += tpl2.format(menu['url'], 'active' if menu['opened'] else "", menu['caption'])
print("***", menu_str)
else:
# 菜单
if menu['child']:
child_html
= menu_tree(menu['child'])
print("----", child_html)
else:
child_html
= ""
print("111")
menu_str
+= tpl1.format(menu['caption'], child_html, "" if menu['opened'] else 'hide')
print("AAAAA", menu_str)
print(123)
return menu_str


menu_html
= menu_tree(result)
print("xx", menu_html)
View Code

二. 组件

生成公共app
- 权限限制
- 生成菜单
python3 manage.py startapp rbac
用户权限 (知识点)用户权限 (知识点)
#白名单url, 不验证

VALID_URL = [
'/app01/.*',
'/app02/.*'
'/login.html'
'/logout.html'
]
config.py
用户权限 (知识点)用户权限 (知识点)
from django.db import models


class User(models.Model):
"""
用户表
"""
username
= models.CharField(verbose_name='用户名', max_length=32)
password
= models.CharField(verbose_name='密码', max_length=64)
email
= models.EmailField(verbose_name='邮箱')

def __str__(self):
return self.username


class Role(models.Model):
"""
角色表
"""
caption
= models.CharField(verbose_name='角色', max_length=32)

def __str__(self):
return self.caption


class User2Role(models.Model):
"""
用户角色关系表
"""
user
= models.ForeignKey(User, verbose_name='用户', related_name='roles')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='users')

def __str__(self):
return '%s-%s' % (self.user.username, self.role.caption,)


class Menu(models.Model):
"""
菜单表
"""
caption
= models.CharField(verbose_name='菜单名称', max_length=32)
parent
= models.ForeignKey('self', verbose_name='父菜单', related_name='p', null=True, blank=True)

def __str__(self):
prev
= ""
parent
= self.parent
while True:
if parent:
prev
= prev + '-' + str(parent.caption)
parent
= parent.parent
else:
break
return '%s-%s' % (prev, self.caption,)


class Permission(models.Model):
"""
权限
"""
caption
= models.CharField(verbose_name='权限', max_length=32)
url
= models.CharField(verbose_name='URL正则', max_length=128)
menu
= models.ForeignKey(Menu, verbose_name='所属菜单', related_name='permissions',null=True,blank=True)

def __str__(self):
return "%s-%s" % (self.caption, self.url,)


class Action(models.Model):
"""
操作:增删改查
"""
caption
= models.CharField(verbose_name='操作标题', max_length=32)
code
= models.CharField(verbose_name='方法', max_length=32)

def __str__(self):
return self.caption


class Permission2Action2Role(models.Model):
"""
权限操作关系表
"""
permission
= models.ForeignKey(Permission, verbose_name='权限URL', related_name='actions')
action
= models.ForeignKey(Action, verbose_name='操作', related_name='permissions')
role
= models.ForeignKey(Role, verbose_name='角色', related_name='p2as')

class Meta:
unique_together
= (
(
'permission', 'action', 'role'),
)

def __str__(self):
return "%s-%s-%s" % (self.permission, self.action, self.role,)
models.py
用户权限 (知识点)用户权限 (知识点)
#验证中间件


from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
from rbac import config
import re


class RbacMiddleware(MiddlewareMixin):

def process_request(self,request,*args,**kwargs):
for pattern in config.VALID_URL:
if re.match(pattern,request.path_info):
return None

action
= request.GET.get('md') # GET
user_permission_dict = request.session.get('user_permission_dict')
if not user_permission_dict:
return HttpResponse('无权限')

# action_list = user_permission_dict.get(request.path_info)
flag = False
for k,v in user_permission_dict.items():
if re.match(k,request.path_info):
if action in v:
flag
= True
break
if not flag:
return HttpResponse('无权限')
/middleware/md.py
用户权限 (知识点)用户权限 (知识点)
import re
from rbac import models
from django.utils.safestring import mark_safe

def permission_session(user_id,request):
"""

:param user_id: rbac中的user表中一条数据id
:param request:
:return:
"""
# obj = models.User.objects.filter(username='杨明').first()
#
# # x = models.User2Role.objects.filter(user_id=obj.id)
# # [User2Role,User2Role,User2Role]
#
# role_list = models.Role.objects.filter(users__user_id=obj.id)
# # [Role,]
# from django.db.models import Count
# # permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').annotate(c=Count('id'))
# permission_list = models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__url','action__code').distinct()
"""
[
{permission_url: '/index.html', action_code:'GET'},
{permission_url: '/index.html', action_code:'POST'},
{permission_url: '/index.html', action_code:'DEL'},
{permission_url: '/index.html', action_code:'Edit'},
{permission_url: '/order.html', action_code:'GET'},
{permission_url: '/order.html', action_code:'POST'},
{permission_url: '/order.html', action_code:'DEL'},
{permission_url: '/order.html', action_code:'Edit'},
]
放在Session中
/index.html?md=GET

{
'/index.html': [GET,POST,DEL,Edit],
'/order.html': [GET,POST,DEL,Edit],
}

"""

user_permission_dict
= {
'/ah-index.html': ["GET","POST","DEL","Edit"],
'/order.html': ["GET","POST","DEL","Edit"],
'/index-(\d+).html': ["GET","POST","DEL","Edit"],
}

request.session[
'user_permission_dict'] = user_permission_dict


def menu(user_id,current_url):
"""
根据用户ID,当前URL:获取用户所有菜单以及权限,是否显示,是否打开
:param user_id:
:param current_url:
:return:
"""
# 所有菜单:处理成当前用关联的菜单
all_menu_list = models.Menu.objects.all().values('id','caption','parent_id')
user
= models.User.objects.filter(id=user_id).first()
role_list
= models.Role.objects.filter(users__user=user)
permission_list
= models.Permission2Action2Role.objects.filter(role__in=role_list).values('permission__id','permission__url','permission__menu_id','permission__caption').distinct()
##### 将权限挂靠到菜单上 ########
all_menu_dict = {}
for row in all_menu_list:
row[
'child'] = [] # 添加孩子
row['status'] = False # 是否显示菜单
row['opened'] = False # 是否默认打开
all_menu_dict[row['id']] = row

for per in permission_list:
if not per['permission__menu_id']:
continue

item
= {
'id':per['permission__id'],
'caption':per['permission__caption'],
'parent_id':per['permission__menu_id'],
'url': per['permission__url'],
'status': True,
'opened': False
}
if re.match(per['permission__url'],current_url):
item[
'opened'] = True
pid
= item['parent_id']
all_menu_dict[pid][
'child'].append(item)

# 将当前权限前辈status=True
temp = pid # 1.父亲ID
while not all_menu_dict[temp]['status']:
all_menu_dict[temp][
'status'] = True
temp
= all_menu_dict[temp]['parent_id']
if not temp:
break

# 将当前权限前辈opened=True
if item['opened']:
temp1
= pid # 1.父亲ID
while not all_menu_dict[temp1]['opened']:
all_menu_dict[temp1][
'opened'] = True
temp1
= all_menu_dict[temp1]['parent_id']
if not temp1:
break
# ############ 处理菜单和菜单之间的等级关系 ############
result = []
for row in all_menu_list:
pid
= row['parent_id']
if pid:
all_menu_dict[pid][
'child'].append(row)
else:
result.append(row)


##################### 结构化处理结果 #####################
for row in result:
print(row['caption'],row['status'],row['opened'],row)


def menu_tree(menu_list):
tpl1
= """
<div class='menu-item'>
<div class='menu-header'>{0}</div>
<div class='menu-body {2}'>{1}</div>
</div>
"""
tpl2
= """
<a href='{0}' class='{1}'>{2}</a>
"""

menu_str
= ""
for menu in menu_list:
if not menu['status']:
continue
# menu: 菜单,权限(url)
if menu.get('url'):
# 权限
menu_str += tpl2.format(menu['url'],'active' if menu['opened'] else "",menu['caption'])
else:
# 菜单
if menu['child']:
child_html
= menu_tree(menu['child'])
else:
child_html
= ""
menu_str
+= tpl1.format(menu['caption'], child_html,"" if menu['opened'] else 'hide')

return menu_str
menu_html
= menu_tree(result)
return menu_html


# simple_tag
def css():
v
= """
<style>
.hide{
display: none;
}
.menu-body{
margin-left: 20px;
}
.menu-body a{
display: block;
}
.menu-body a.active{
color: red;
}
</style>
"""
return v

# simple_tag
def js():
v
= """
<script>
$(function(){

$('.menu-header').click(function(){
$(this).next().removeClass('hide').parent().siblings().find('.menu-body').addClass('hide');

})

})
</script>
"""
return v
service.py

a. 以后调用 

用户权限 (知识点)用户权限 (知识点)
#自定义的中间件加入到setting

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.md.RbacMiddleware',
]
setting.py 
#后端
from rbac import service

1. 用户登录后,拿到用户的ID,调用permission_session()函数(函数代码还没写)
函数获取用户角色的权限,格式如:
“”“
user_permission_dict = {
'/ah-index.html': ["GET","POST","DEL","Edit"],
'/order.html': ["GET","POST","DEL","Edit"],
'/index-(\d+).html': ["GET","POST","DEL","Edit"],
}
”“”

def login():
  permission_session(用户ID,request)
  return .....

2.setting中加入中间件,如上
  

3.#获取菜单
current_url= request.pathinfo
menu_list = service.menu(用户ID,current_url)

4.尽量用simple_tag
css = servicr.css()
js = servicr.js()

5.前端

{{ css|safe }}
{{ menu_list|safe }}
{{ js|safe }}  

代码  http://files.cnblogs.com/files/oyoui/rbac.zip  

三. 项目

teacher

self