CRM项目之RBAC权限组件-day26

写在前面



    世间安得双全法 不负如来不负卿



 s17day26 CRM项目

 项目概要:XX公司CRM
- 权限管理,公共组件,app *****
- 熟悉增删改查,Low ***
- 增删改查组件,公共组件,app **** 内容回顾:
. .all,values,values_list
models.xx.objects.all()
---> [obj,obj,obj,]
models.xx.objects.values('id','name')
---> [{"id":,'name':'兴普'},{"id":,'name':'兴普1'},]
models.xx.objects.values_list('id','name')
---> [(,'兴普'),(,'记不住就别来了')] . 中间件是什么?有什么作用? class Md1: def process_request(self,request):
pass class process_response(self,request,response):
return response class Md2: def process_request(self,request):
pass class process_response(self,request,response):
return response MIDDLEWARE = [ "xxxxx.xxxx.Md1",
"xxxxx.xxxx.Md2"
] . ORM创建表 FK常用操作
class A(Model):
name = models.CharField(...) class B(Model):
name = models.CharField(...)
fk = models.FK(A) a. B表中有几列
id
name
fk_id b. 跨表操作
b_list = models.B.objects.all()
for item in b_list:
item.id
item.name
item.fk_id
item.fk
item.fk.name
item.fk.id
c. 跨表操作
b_list = models.B.objects.values('id','name','fk_id','fk__name')
for item in b_list:
item['id']
item['name']
item['fk_id']
item['fk__name'] d. 跨表操作
b_list = models.B.objects.vlaues_list('id','name','fk_id','fk__name')
for item in b_list:
item[]
item[]
item[']
item[]
e. 找A名称=“吴一飞”所有B表中的数据
models.B.objects.filter(fk__name='吴一飞') M2M常用操作: class A(Model):
name = models.CharField(...) class B(Model):
name = models.CharField(...)
m = models.M2M(A) a. B表中有几列
id
name PS: 自动生成一个第三张表;m用于间接的对第三张表进行操作 b. 在B表中插入三条数据;A表中插入2条数据
models.A.objects.create(name='赵峰分')
models.A.objects.create(name='王勇')
models.A.objects.create(name='刘宏伟') models.B.objects.create(name='兴普')
models.B.objects.create(name='香姐')
models.B.objects.create(name='名扬') c. 兴普和【赵峰分,王勇,刘宏伟】创建关系 obj = models.B.objects.create(name='兴普')
obj.m.add()
obj.m.add()
obj.m.add()
obj.m.add(*[,,]) d. 查找和兴普有关系的男人?
obj = models.B.objects.create(name='兴普')
obj.m.all() [A—obj,A—obj,A—obj] . Session是什么?和Cookie有什么区别? . 正则表达式 re.match() ^ $ 示例程序:pro_crm
员工模块:
权限,根据含有正则表达式URL进行分配
操作,通过配置文件进行定制
学生模块:
问卷 今日内容: 权限:授权 s17crm
- app01
- rbac
- models.py 第一版:
. 权限表(含有正则表达式),permission id url title
/userinfo/ 用户列表
/userinfo/(\d+)/delete/ 删除用户
/userinfo/(\d+)/change/ 修改用户
/userinfo/add/ 添加用户 . 用户表,userinfo
id username password
yql
wxp
zmx
ll . 用户和权限关系表
id 用户ID 权限ID 问题:用户分配权限时,麻烦 第二版: . 权限组,group
id title
用户组
订单组 . 权限表(含有正则表达式),permission id url title code group_i1 is_menu
/userinfo/ 用户列表 list true
/userinfo/(\d+)/delete/ 删除用户 del false
/userinfo/(\d+)/change/ 修改用户 edit false
/userinfo/add/ 添加用户 add true
/order/add/ ... add true
/order/(\d+)/delete/ del false
/order/(\d+)/change/ edit false
/order/ list true . 用户表,userinfo
id username password
yql
wxp
zmx
ll . 角色表,role
id title
销售员
销售经理
市场专员
市场经理
IT
CTO
销售市场总监
CEO . 用户角色关系表
用户ID 角色ID . 角色和权限关系表
角色ID 权限ID 注意:根据用户找权限
- 用户具有的所有角色
- 再根据角色找所有权限(去重) 中午作业:
- 填充数据
- id=1的用户,
- 找具有所有角色
- 找到所有权限
- 找到所有权限(去重) 填充数据:
基于Django Admin数据填充 补充:不要相信你的眼睛 分配权限:
销售员:用户列表
孟祥杰
王勇 销售经理:用户列表,添加用户
银秋良 总监: 用户列表,添加用户,订单列表
兴普 CEO:....
刘磊 注意:Djang Admin进行添加权限或授权 权限: 自动生成菜单 . 获取数据 [
{'menu_id': , 'menu_title': '用户管理菜单', 'title': '用户列表', 'url': '/userinfo/'},
{'opened':True,'menu_id': , 'menu_title': '用户管理菜单', 'title': '订单列表', 'url': '/order/'}, {'menu_id': , 'menu_title': '权限管理菜单', 'title': 'xxx列表', 'url': '/order/add/'}
{'menu_id': , 'menu_title': '权限管理菜单', 'title': 'xx列表', 'url': '/order/add/'}
] . 根据数据创建两级菜单
用户管理菜单
用户列表!
订单列表
权限管理菜单
xxx列表
xx列表 思路:
page_menu = {
:{'menu_id':,'menu_title':'用户管理菜单','children': [{ 'title': '用户列表', 'url': '/userinfo/'},{'title': '订单列表', 'url': '/order/'} ]}
: {'menu_id':,'menu_title':'权限管理菜单','children': [{ 'title': '用户列表', 'url': '/userinfo/'},{'title': '订单列表', 'url': '/order/'} ]}
} current_url = request.path_info for item in permission_menu_list:
url = item['url']
regex = xxx.format(url)
if re.match(regex,current_url):
item['opened'] = True menu_id = item['menu_id']
opened = "active" if item.get('opened') else "" child = {'title':item['title'],'url':item['url'],'opened': opened}
if menu_id in page_menu:
page_menu[menu_id]['children'].append(child)
if opened:
page_menu[menu_id]['opened'] = opened
else:
page_menu[menu_id] = {'opened':opened, 'menu_title': item['menu_title'], 'children': [child, ]} 总结: . 表结构设计 . 录入数据 . 用户登录:
- 获取角色
- 获取权限
- 对权限去重 . 结构,权限信息
{
: {
'urls': ['/userinfo/', '/userinfo/add/', '/userinfo/(\\d+)/delete/', '/userinfo/(\\d+)/change/'],
'codes': ['list', 'add', 'del', 'edit']
},
: {
'urls': ['/order/', '/order/add/', '/order/(\\d+)/delete/', '/order/(\\d+)/change/'],
'codes': ['list', 'add', 'del', 'edit']
} }
放入session中
. 中间件
白名单
request.path_info
session获取权限,进行验证 . 自动生成菜单
- inclusion_tag
- 母版 . Django Admin 权限app中写代码:
- 中间件
- init_permission
- models
- admin
- templatetags 依赖配置文件:
XX = "permission_dict"
OO = "permission_menu_list"
RBAC_LOGIN_URL = "/login/"
URL_FORMAT = "^{0}$" VALID_URL_LIST = [
"^/login/$",
"^/admin.*",
] 使用权限管理:
. 创建cmdb project
. rbac拷贝到项目中
. 在settings.APP中将 rbac 注册
. 在settings中设置
XX = "permission_dict"
OO = "permission_menu_list"
RBAC_LOGIN_URL = "/login/"
URL_FORMAT = "^{0}$" VALID_URL_LIST = [
"^/login/$",
"^/admin.*",
]
. 开发CMDB完成 . 中间件中注册
. 利用temaplatetags和母版获取动态菜单 . 在Django Admin中操作权限信息 作业:
. 权限系统:4天
. 自己创建一个主机管理系统:
- 主机
- 主机组
- 部门
- 机房
. 使用 权限系统[10天]

武Sir - 笔记

一、内容回顾

- .all  .values  .values_list
models.xxx.objects.all() --> [obj,obj,obj,...]
models.xxx.objects.values('id','name') --> [{'id':1,'name':'alex'},{'id':2,'name':'standby'},...]
models.xxx.objects.values_list('id','name') --> [(1,'alex'),(2,'standby',....)] - 中间件是什么?有什么用?
可以拦截所有的请求和响应,进行一些处理
中间件执行时有序的,从上到下执行,如果当前中间件不允许过,那么就不会再执行后面的中间件
比如说:对用户的请求做下黑名单 process_request() 默认没有返回值,即返回None; 则继续往下执行
但是如果有返回值,则不再继续往下执行,直接返回; 每个中间件就是一个类:
class Md1:
def process_request(self,request):
pass
def process_response(self,request,response):
return response
class Md2:
def process_request(self,request):
pass
def process_response(self,request,response):
return response settings.py里配置 MIDDLEWARE = [
"XXXXXX.XXXXX.Md1",
"XXXXXX.XXXXX.Md2",
] - ORM创建表 - ForeignKey常用操作
class A(Model):
name = models.CharField(...) class B(Model):
name = models.CharField(...)
fk = models.ForeignKey(A) a. B表中有几列:
id
name
fk_id b. 跨表操作
b_list = models.B.objects.all()
for item in b_list:
item.id
item.name
item.fk
item.fk_id
item.fk.id
item.fk.name c. 跨表操作
b_list = models.B.objects.values('id','name','fk_id','fk__name')
for item in b_list:
item['id']
item['name']
item['fk_id']
item['fk__name'] d. 跨表操作
b_list = models.B.objects.vlaues_list('id','name','fk_id','fk__name')
for item in b_list:
item[0]
item[1]
item[2]
item[3] e. 找A表名称等于"alex"的所有B表中的数据 *****
models.B.objects.filter(fk__name="alex") good models.B.objects.filter(fk.name="alex") 这个是不可以,需要验证!!!!!! - M2M常用操作
class A(Model):
name = models.CharField(...) class B(Model):
name = models.CharField(...)
m = models.ManyToMany(A) a. B表中有几列?
id
name PS:ManyToMany 会自动生成第三张表,字段m用于间接的对第三张表进行操作 b. 在B表中插入3条数据,A表中插入2条数据 models.A.objects.create(name='alex1')
models.A.objects.create(name='alex2') models.B.objects.create(name='egon1')
models.B.objects.create(name='egon2')
models.B.objects.create(name='egon3') c. 让 egon1 和 [alex1,alex2] 创建关系
obj = models.B.objects.filter('name'='egon1')
obj.m.add(*[1,2]) d. 查找和egon1有关系的人
obj = models.B.objects.filter('name'='egon1')
obj.m.all() # 拿到了queryset对象集合,都是A表的对象 - Session是什么? 和cookie有什么区别?
- session依赖cookie而存在
- session是存储在服务端的键值对
- cookie是存储在客户端浏览器里的键值对 - 用户第一次来访问主页,服务端生成一个随机字符串通过cookie返回给客户端
同时服务端把这个字符串当做key存储在数据库表里(也可以存在其他地方),值可以为空 - 用户第二次发起登录请求则带上刚才的cookie和用户信息
服务端则把用户信息(比如用户名密码)用某种方式存储在刚才随机字符串对应的值里
这样就表示用户登录过了

  

二、CRM项目介绍

  - CRM简介

xxx公司的CRM项目
- 权限管理 (写成一个公共的组件/app,适用于Django项目) *****
- low的增删改查 ***
- 增删改查的组件 (写成公共组件/app) **** - 员工模块
- 权限:根据 含有正则表达式的url 进行分配的
- 操作:通过配置文件进行定制
- 学生模块
- 问卷

  - RBAC简介

RBAC(Role-Based Access Control,基于角色的权限控制)
就是用户通过角色与权限进行关联。 简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。
这样,就构造成“用户-角色-权限”的授权模型。 在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。

  

三、表结构设计

- 第一版
- permission权限表(含有正则表达式的url)
id url title
1 /userinfo/ 用户列表
2 /userinfo/(\d+)/delete/ 删除用户
3 /userinfo/(\d+)/change/ 修改用户
4 /userinfo/add/ 添加用户 - userinfo用户表
id username password
1 alex 123
2 egon 123
3 standby 123 - 用户和权限的关系表
id user_id permission_id
1 1 1
2 1 2
3 2 1
4 3 1
5 3 2
6 3 3 问题:用户分配权限时,很麻烦,不方便后面的维护 - 第二版
- permission权限表(含有正则表达式的url)
id url title
1 /userinfo/ 用户列表
2 /userinfo/(\d+)/delete/ 删除用户
3 /userinfo/(\d+)/change/ 修改用户
4 /userinfo/add/ 添加用户 - userinfo用户表
id username password
1 alex 123
2 egon 123
3 standby 123 - role 角色表
id title
1 销售员
2 销售经理
3 市场专员
4 市场经理
5 IT
6 CTO
7 CEO - 用户和角色关系表
id user_id role_id
1 1 1
2 2 1
3 2 2
4 3 1
5 3 2
6 3 7 - 角色和权限的关系表
id role_id permission_id
1 1 1
2 2 1
3 2 4
4 6 1
5 6 3
6 6 4
7 7 1
8 7 2
9 7 3
10 7 4 列出standby所具有的所有权限,可能会重复,所以需要去重;
注意:根据用户找权限
- 根据用户找角色
- 根据角色找权限 - 增加权限组表group
id title
1 用户组
2 订单组 - permission权限表 增加一个代号code字段、权限组字段以及 是否是菜单字段(涉及到在前端页面展示):
- permission权限表(含有正则表达式的url)
id url title code group_id is_menu
1 /userinfo/ 用户列表 list 1 true
2 /userinfo/(\d+)/delete/ 删除用户 del 1 false
3 /userinfo/(\d+)/change/ 修改用户 edit 1 false
4 /userinfo/add/ 添加用户 add 1 true
5 /order/ 订单列表 list 2 true
6 /order/(\d+)/delete/ 删除订单 del 2 false
7 /order/(\d+)/change/ 修改订单 edit 2 false
8 /order/add/ 添加订单 add 2 true

  

class Group(models.Model):
"""
权限组表
"""
title = models.CharField(max_length=32) class Meta:
verbose_name_plural = "权限组表"
def __str__(self):
return self.title
class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题',max_length=32)
url = models.CharField(verbose_name="含正则的URL",max_length=128)
code = models.CharField(verbose_name="权限代号",max_length=16)
group = models.ForeignKey(verbose_name="权限组",to="Group") # 把具体的权限分组管理,类似省市县
is_menu = models.BooleanField(verbose_name="是否是菜单") # 用来筛选出可以在左侧菜单展示的选项
class Meta:
verbose_name_plural = "权限表" def __str__(self):
return self.title class UserInfo(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name="用户名",max_length=32)
password = models.CharField(verbose_name='密码',max_length=64)
roles = models.ManyToManyField(verbose_name='具有所有角色',to="Role",blank=True) class Meta:
verbose_name_plural = "用户表" def __str__(self):
return self.username class Role(models.Model):
"""
角色表
"""
title = models.CharField(verbose_name='角色名称',max_length=32)
permissions = models.ManyToManyField(verbose_name="具有所有权限",to='Permission',blank=True) class Meta:
verbose_name_plural = "角色表" def __str__(self):
return self.title

  

CRM项目之RBAC权限组件-day26  CRM项目之RBAC权限组件-day26

让组和菜单创建关系     页面显示的时候是不显示组的

组的存在只是为了在分配权限的时候方便一些:一个组下面的增删改查

但是菜单的信息存储在哪呢?

- 可以把菜单和组都存储在  Group表里

- 增加parent字段,让某些组属于某个菜单(ORM里就是自关联,需要related_name字段)

- 然后再增加一个is_group字段用来区分组和菜单

补充:

- 具体的权限只能在 组 下面,不能直接挂载菜单下面

class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题',max_length=32)
url = models.CharField(verbose_name="含正则的URL",max_length=128)
code = models.CharField(verbose_name="权限代号",max_length=16)
# group = models.ForeignKey(verbose_name="权限组",to="Group") # 把具体的权限分组管理,类似省市县
group = models.ForeignKey(verbose_name="权限组",to="Group",limit_choices_to={'is_group':True})
is_menu = models.BooleanField(verbose_name="是否是菜单") # 用来筛选出可以在左侧菜单展示的选项
class Meta:
verbose_name_plural = "权限表" def __str__(self):
return self.title

  

group = models.ForeignKey(verbose_name="权限组",to="Group")
group = models.ForeignKey(verbose_name="权限组",to="Group",limit_choices_to={'is_group':True})

就是说:只有是组的时候才能做 Permission表的外键

 最终的表结构如下:

class Group(models.Model):
"""
权限组表
"""
title = models.CharField(max_length=32)
parent = models.ForeignKey(to="Group",related_name="xxx",null=True,blank=True)
is_group = models.BooleanField(verbose_name="是否是组",default=True) class Meta:
verbose_name_plural = "权限组表"
def __str__(self):
return self.title class Permission(models.Model):
"""
权限表
"""
title = models.CharField(verbose_name='标题',max_length=32)
url = models.CharField(verbose_name="含正则的URL",max_length=128)
code = models.CharField(verbose_name="权限代号",max_length=16)
# group = models.ForeignKey(verbose_name="权限组",to="Group") # 把具体的权限分组管理,类似省市县
group = models.ForeignKey(verbose_name="权限组",to="Group",limit_choices_to={'is_group':True})
is_menu = models.BooleanField(verbose_name="是否是菜单") # 用来筛选出可以在左侧菜单展示的选项 class Meta:
verbose_name_plural = "权限表" def __str__(self):
return self.title class UserInfo(models.Model):
"""
用户表
"""
username = models.CharField(verbose_name="用户名",max_length=32)
password = models.CharField(verbose_name='密码',max_length=64)
roles = models.ManyToManyField(verbose_name='具有所有角色',to="Role",blank=True) class Meta:
verbose_name_plural = "用户表" def __str__(self):
return self.username class Role(models.Model):
"""
角色表
"""
title = models.CharField(verbose_name='角色名称',max_length=32)
permissions = models.ManyToManyField(verbose_name="具有所有权限",to='Permission',blank=True) class Meta:
verbose_name_plural = "角色表" def __str__(self):
return self.title

  

四、需求分析

# 几点注意事项

- 双下划线跨表取值
- 外键和多对多 都可以用 __(双下划线) 跨表获取对应字段
- user -> 多个角色roles -> 根据角色的permissions字段加 '__' 跨到权限表 ```
permission_list = user.roles.filter(permissions__id__isnull=False).values(
'permissions__id',
'permissions__title',
'permissions__url',
'permissions__code',
'permissions__group',
'permissions__is_menu'
).distinct()
``` - 列表distinct()去重
- [].distinct() 进行去重 - 自定义配置
- 在settings.py里定义好 (变量名一定要大写)
- 在代码里引入
Django的所有配置(既包含自定义的也包含Django内置的):
from django.conf import settings - 找到某个用户所具有的权限
- 先查找所具有的角色
- 再查找所具有的权限
- 最后把权限去重 - 自动生成权限菜单
- 获取数据
- 创建两级菜单
- 两级菜单生成
- 在视图函数中拼接好返回给前端
- @register.simple_tag()
- @register.inclusion_rag("menu_tpl.html") - 请求url验证中间件
- 要在settings.py里维护一份白名单
- 请求验证中间件依赖于session,所以自定义中间件的位置应该放在session中间件(SessionMiddleware)之后
- 对用户请求的url和该用户所具有的权限要做正则匹配:re.match(patten, current_request_url)

 

# 1/2/3 是组ID
{
1: {
'urls': ['/userinfo/', '/userinfo/add/'],
'codes': ['list', 'add']
},
2: {
'urls': ['/order/'],
'codes': ['list']
},
3: {
'urls': ['/index/'],
'codes': ['index']
}
} [
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/userinfo/', 'permission_title': '用户列表'},
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/userinfo/add/', 'permission_title': '添加用户'},
{'menu_id': 6, 'menu_title': '权限管理菜单', 'permission_url': '/permissioninfo/', 'permission_title': '权限列表'},
{'menu_id': 6, 'menu_title': '权限管理菜单', 'permission_url': '/permissioninfo/add', 'permission_title': '添加权限'}
] # 4/6 是菜单ID
# 菜单和组都在同一张表里,有父子关系,最后的目的是构造菜单和is_menu=True的权限item的两级菜单
{
4:{
'opened':False,
'menu_title':'用户管理菜单',
'children': [
{'title': '用户列表', 'url': '/userinfo/', 'opened':False},
{'title': '订单列表', 'url': '/order/', 'opened':False,}
]
},
6: {
'opened':False,
'menu_title':'权限管理菜单',
'children': [
{'title': '权限列表', 'url': '/userinfo/', 'opened':False},
{'title': '添加权限', 'url': '/order/', 'opened':False,}
]
}
}
总结:
- 表的设计(很重要)
4个类 6张表 - Django admin 录入数据 - 用户登录处理
- 获取角色
- 获取权限
- 对权限去重
- 然后把权限数据存储到session - 登录中间件
- 白名单
- request.path_info
- 从session获取权限,进行验证 - 自动生成菜单
- 跨表取出相关数据,转换字典结构
- 视图中生成好字典返回给前端
- 前端模板渲染 - simple_tag
- inclusion_rag
- 模板layout.html - 权限app包含哪些内容
- 中间件
- init_permission
- models
- admin
- templatetags 依赖配置文件 PERMISSION_DICT = "permission_dict"
URL_FORMAT = "^{0}$"
RBAC_LOGIN_URL = "/login/"
VALID_URL_LIST = [
"^/login/$",
"^/admin.*",
] - 使用rbac权限管理
- 创建CMDB项目
- 把rbac拷贝到项目中
- 在settings.APP中注册 rbac
- 在settings中配置相关变量:
PERMISSION_DICT = "permission_dict"
URL_FORMAT = "^{0}$"
RBAC_LOGIN_URL = "/login/"
VALID_URL_LIST = [
"^/login/$",
"^/admin.*",
]
- 开发CMDB
- 开启中间件验证
- 利用tmplatetags和模板动态生成菜单
- 在Django admin中操作权限信息 练习
- 权限系统补充完整,搞清楚每一行代码 [4天]
- 自己创建project,比如主机管理系统
- 主机
- 主机组
- 部门
- 机房
- ...
- 使用rbac权限系统 [10天]

  

五、代码实现

表结构如上所述,此处略过...

# 项目结构

D:\soft\work\Python_17\day26\homework\s17crm>tree /F
卷 NewDisk 的文件夹 PATH 列表
卷序列号为 2E8B-8205
D:.
│ db.sqlite3
│ manage.py


├─app01
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ └─ __init__.py


├─rbac
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ │ __init__.py
│ │
│ ├─middleware
│ │ └─ rbac.py
│ │
│ │
│ ├─service
│ │ │ init_permission.py
│ │ │
│ │ └─__pycache__
│ │ init_permission.cpython-35.pyc
│ │
│ ├─templatetags
│ │ menu_gennerator.py
│ └─ __init__.py


├─s17crm
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ └─ __init__.py

├─statics
│ ├─css
│ │ bbs.css
│ │ bootstrap-select.min.css
│ │ bootstrap-theme.css
│ │ bootstrap-theme.css.map
│ │ bootstrap-theme.min.css
│ │ bootstrap-theme.min.css.map
│ │ bootstrap.css
│ │ bootstrap.css.map
│ │ bootstrap.min.css
│ │ bootstrap.min.css.map
│ │ bootstrapValidator.min.css
│ │
│ ├─image
│ │ header.png
│ │
│ └─js
│ bootstrap-select.js.map
│ bootstrap-select.min.js
│ bootstrap.js
│ bootstrap.min.js
│ bootstrapValidator.min.js
│ city_info.js
│ jquery-3.2.1.js
│ jquery-3.2.1.min.js
│ jquery.cookie.js
│ npm.js

└─templates
index.html
login.html
menu_tpl.html
order.html
order_add.html
tpl.html
user.html
user_add.html D:\soft\work\Python_17\day26\homework\s17crm>

  

# s17crm/app01/views.py

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
from django.forms import widgets
from rbac import models
from rbac.service.init_permission import init_permission
from django.conf import settings class LoginForm(Form):
username = fields.CharField(required=True,error_messages={'required':"用户名不能为空"})
password = fields.CharField(required=True,error_messages={'required':"密码不能为空"}) def login(request):
if "GET" == request.method:
form = LoginForm()
return render(request,'login.html',{'form':form})
else:
form = LoginForm(data=request.POST)
if form.is_valid():
# 格式验证通过
# 注意:只有当 LoginForm 里面变量名称和models表字段名称一致才能使用 **form.cleaned_data
user = models.UserInfo.objects.filter(**form.cleaned_data).first()
if user:
# 获取当前用户的权限信息做格式化然后存储到session中
init_permission(request,user)
print(request.session.get(settings.PERMISSION_DICT))
print(request.session.get(settings.PERMISSION_MENU_LIST))
return redirect('/index/')
else:
from django.core.exceptions import ValidationError
form.add_error('password',ValidationError("用户名或密码错误!!!")) return render(request,'login.html',{'form':form}) def index(request):
# 通过 @register.inclusion_tag('menu_tpl.html') 构造菜单结构并渲染
# 所以这里不用做处理
return render(request,'index.html') def clear(request):
# 手动清空session
request.session.clear()
return HttpResponse("已清除") def order(request):
return render(request,'order.html') def order_add(request):
return render(request,'order_add.html') def user(request):
return render(request,'user.html') def user_add(request):
return render(request,'user_add.html')

  

# s17crm\rbac\middleware\rbac.py
# 用户访问鉴权中间件 #!/usr/bin/python
# -*- coding:utf-8 -*-
# 这是页面权限验证的中间件 from django.shortcuts import HttpResponse,redirect
from django.conf import settings
import re class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__() def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response class RbacMiddleware(MiddlewareMixin):
def process_request(self,request): # 1. 获取当前请求的 uri
current_request_url = request.path_info # 2. 判断是否在白名单里,在则不进行验证,直接放行
for url in settings.VALID_URL_LIST:
if re.match(url, current_request_url):
return None # 3. 验证用户是否有访问权限
flag = False
permission_dict = request.session.get(settings.PERMISSION_DICT) print(permission_dict) # 如果没有登录过就直接跳转到登录页面
if not permission_dict:
return redirect(settings.RBAC_LOGIN_URL)
"""
{
1: {
'codes': ['list', 'add'],
'urls': ['/userinfo/', '/userinfo/add/']
},
2: {
'codes': ['list'],
'urls': ['/order/']
}
}
"""
for group_id, values in permission_dict.items():
for url in values['urls']:
# 必须精确匹配 URL : "^{0}$"
patten = settings.URL_FORMAT.format(url)
if re.match(patten, current_request_url):
flag = True
break
if flag:
break
if not flag:
return HttpResponse("无权访问")

  

# s17crm\rbac\service\init_permission.py
# 这是用户登录后初始化用户权限信息的模块 #!/usr/bin/python
# -*- coding:utf-8 -*- from django.conf import settings def init_permission(request,user):
# 获取当前用户的权限信息
"""
外键和多对多 都可以用 __(双下划线) 跨表获取对应字段
[].distinct() 进行去重
"""
permission_list = user.roles.filter(permissions__id__isnull=False).values(
'permissions__id',
'permissions__title',
'permissions__url',
'permissions__code',
'permissions__group',
'permissions__is_menu',
'permissions__group__parent_id', # 即:当前权限所在组的所属菜单ID
'permissions__group__parent__title', # 即:当前权限所在组的所属菜单名称
).distinct() # 格式化当前用户的权限信息
"""
{
1: {
'codes': ['list', 'add'],
'urls': ['/userinfo/', '/userinfo/add/']
},
2: {
'codes': ['list'],
'urls': ['/order/']
}
}
"""
permission_dict = {}
for item in permission_list:
url = item.get('permissions__url')
code = item.get('permissions__code')
group_id = item.get('permissions__group')
if group_id in permission_dict:
permission_dict[group_id]['urls'].append(url)
permission_dict[group_id]['codes'].append(code)
else:
permission_dict[group_id] = {'codes': [code, ], 'urls': [url, ]}
# 把当前用户权限信息存储到session中
request.session[settings.PERMISSION_DICT] = permission_dict # 格式化当前用户的菜单列表信息
# 最终的目的就是要把菜单和具体的is_menu=True的权限item关联起来并在前端构成 两级菜单
'''
[
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/userinfo/', 'permission_title': '用户列表'},
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/userinfo/add/', 'permission_title': '添加用户'},
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/order/', 'permission_title': '订单列表'}
]
'''
permission_menu_list = []
for item in permission_list:
if item['permissions__is_menu']:
tmp = {
'menu_title':item['permissions__group__parent__title'],
'menu_id':item['permissions__group__parent_id'],
'permission_url':item['permissions__url'],
'permission_title':item['permissions__title'],
}
permission_menu_list.append(tmp)
# 把当前用户菜单信息存储到session中
request.session[settings.PERMISSION_MENU_LIST] = permission_menu_list # 设置session的超时时间:30min
request.session.set_expiry(1800) # 为了要在前端构造出两级菜单,还需要对上面拿到的菜单列表做一下格式转换
'''
{
4:{
'opened':False,
'menu_title':'用户管理菜单',
'children': [
{'title': '用户列表', 'url': '/userinfo/', 'opened':False},
{'title': '订单列表', 'url': '/order/', 'opened':False,}
]
},
6: {
'opened':False,
'menu_title':'权限管理菜单',
'children': [
{'title': '权限列表', 'url': '/xxxinfo/', 'opened':False},
{'title': '分类列表', 'url': '/xxxxxxxx/', 'opened':False,}
]
}
}
'''
# s17crm\rbac\templatetags\menu_gennerator.py
# 使用inclusion_tag渲染菜单显示在前端页面 #!/usr/bin/python
# -*- coding:utf-8 -*- from django import template
register = template.Library() import re
from django.conf import settings '''
[
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/userinfo/', 'permission_title': '用户列表'},
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/userinfo/add/', 'permission_title': '添加用户'},
{'menu_id': 4, 'menu_title': '用户管理菜单', 'permission_url': '/order/', 'permission_title': '订单列表'},
...
] 转换成下面的结构,然后再前端进行展示 {
4:{
'opened':False,
'menu_title':'用户管理菜单',
'children': [
{'title': '用户列表', 'url': '/userinfo/', 'opened':False},
{'title': '订单列表', 'url': '/order/', 'opened':False,}
]
},
6: {
'opened':False,
'menu_title':'权限管理菜单',
'children': [
{'title': '权限列表', 'url': '/xxxinfo/', 'opened':False},
{'title': '分类列表', 'url': '/xxxxxxxx/', 'opened':False,}
]
},
...
}
''' @register.inclusion_tag('menu_tpl.html')
def menu_show(request):
permission_menu_dict = {}
current_request_url = request.path_info
permission_menu_list = request.session.get(settings.PERMISSION_MENU_LIST) for item in permission_menu_list:
url = item['permission_url']
patten = settings.URL_FORMAT.format(url)
# 首先遍历所有的具体权限,与当前请求url做比较,匹配则把 opened置为 active,用于配合前端的 .active 属性
opened = "active" if re.match(patten, current_request_url) else "" menu_id = item['menu_id']
child = {'opened': opened, 'title': item['permission_title'], 'url': item['permission_url']}
if menu_id in permission_menu_dict:
# 更新已有menu
permission_menu_dict[menu_id]['children'].append(child)
if opened:
permission_menu_dict[menu_id]['opened'] = opened
else:
# 初始化一个menu
permission_menu_dict[menu_id] = {
'opened': opened,
'menu_title': item['menu_title'],
'children': [child,]
}
return {'permission_menu_dict':permission_menu_dict} # s17crm\templates\menu_tpl.html {% for menu_id,item_dict in permission_menu_dict.items %}
<div class="item">
<div class="menu_title">
<a href="#" class="{{ item_dict.opened }}">{{ item_dict.menu_title }}</a>
</div>
<div class="menu_items {{ item_dict.opened }}">
{% for permission in item_dict.children %}
<a href={{ permission.url }} class="{{ permission.opened }}">{{ permission.title }}</a>
{% endfor %}
</div>
</div>
{% endfor %}

  

# s17crm\templates\login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <form method="post" novalidate>
{% csrf_token %}
<P>
{{ form.username }} {{ form.errors.username.0 }}
</P>
<p>
{{ form.password }} {{ form.errors.password.0 }}
</p> <input type="submit" value="提交">
</form> </body>
</html>
# s17crm\templates\tpl.html

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>###CRM</title>
<link rel="Shortcut Icon" href="{% static "image/header.png" %}">
<link rel="stylesheet" href="{% static "css/bootstrap.css" %}">
<script src="{% static "js/jquery-3.2.1.min.js" %}"></script>
<script src="{% static "js/bootstrap.js" %}"></script> <style>
body {
margin: 0;
padding: 0;
}
.header {
height: 40px;
background-color: lightgreen;
text-align: center;
line-height: 40px;
}
.menu {
float: left;
width: 16%;
background-color: wheat;
min-height: 500px
}
.right_body {
float: left;
width: 84%;
min-height: 500px;
}
.item .menu_title {
font-size: 20px;
}
.item .menu_title a.active {
color: green;
}
.item .menu_items {
display: none;
}
.item .menu_items.active {
display: block !important;
}
.item .menu_items a {
font-size: 15px;
display: block;
padding-left: 20px;
}
.item .menu_items a.active {
color: red;
}
</style>
</head>
<body> <div class="header">
<h2>头部文字水平垂直居中显示...</h2>
</div> {% load menu_gennerator %}
<div class="content"> <div class="menu">
{% menu_show request %}
</div> {% block content %} <h1>这个是模板页面...</h1> {% endblock %} </div> </body>
<script>
$(function () {
menu_swith();
}); function menu_swith() {
$(".menu_title").click(function () {
if($(this).children().hasClass("active")) {
$(this).next().removeClass("active");
$(this).children().removeClass("active");
}else {
$(this).children().addClass("active");
$(this).next().addClass("active");
}
})
} </script> </html>

  

# s17crm\templates\index.html

{% extends "tpl.html" %}

{% block content %}

<div class="right_body">
<h1>这是首页...</h1>
</div> {% endblock %} # s17crm\templates\order.html {% extends "tpl.html" %} {% block content %} <div class="right_body">
<h1>订单列表...</h1>
</div> {% endblock %} # s17crm\templates\order_add.html {% extends "tpl.html" %} {% block content %} <div class="right_body">
<h1>新增订单...</h1>
</div> {% endblock %} ...

  

# s17crm\s17crm\urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.login),
url(r'^index/', views.index),
url(r'^clear/', views.clear),
url(r'^userinfo/$', views.user),
url(r'^userinfo/add/$', views.user_add),
url(r'^order/$', views.order),
url(r'^order/add/$', views.order_add),
]

  

# \s17crm\s17crm\settings.py

"""
Django settings for s17crm project. Generated by 'django-admin startproject' using Django 1.11.3. For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/ For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
""" import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '-dulev94(7c=_e_s!@ww937pu3rqiiszw1y(eyl3*mp$&5h_e2' # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
'rbac',
] 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'
] ROOT_URLCONF = 's17crm.urls' TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
] WSGI_APPLICATION = 's17crm.wsgi.application' # Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
} # Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
] # Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' #引用名
# 配置STATICFILES_DIRS,指定额外的静态文件存储位置
STATICFILES_DIRS = (
os.path.join(BASE_DIR,"statics"), #实际名 ,即实际文件夹的名字
) ########################## 自定义权限相关配置 ################################## PERMISSION_DICT = "permission_dict"
PERMISSION_MENU_LIST = "permission_menu_list"
URL_FORMAT = "^{0}$"
RBAC_LOGIN_URL = "/login/"
VALID_URL_LIST = [
"^/login/$",
"^/admin.*",
"^/clear/$",
]

  

上一篇:查看当前session权限


下一篇:Word报告自动生成(例如 导出数据库结构)