一、功能分析:

一个成熟的web应用,对权限的控制、管理是不可少的;对于一个web应用来说是什么权限?

这要从web应用的使用说起,用户在浏览器输入一个url,访问server端,server端返回这个url下对应的资源;

所以 对于用户来说 1个可以访问url 就等于1个权限

比如某人开发了一个web应用包含以下5个url,分别对于不同资源;

1、91.91p15.space/Chinese/

2、91.91p15.space/Japanese and Korean/

3、91p15.space/Euramerican/

4、91p15.space/Latin America/

5、91p15.space/African/

--------------------------------------------------------------------------------------------------------

普通用户:可以访问 5

白金用户:可以访问 4、5、1

黄金用户:可以访问1、2、3、4、5

为什么某些网站会为广大用户做角色划分呢(比如 普通、会员、黑金、白金)?

因为给用户归类后,便于权限的划分、控制、管理;

所以我们把这种基于角色来做得权限控制,称为RBAC(Role Basic Access Control)

二、权限管理数据库表结构设计

1、用户表:用户表和角色表为多对多关系,1个用户可以有多个角色,1个角色可以被多个用户划分;

2、角色表:角色表和权限也是多对多关系,一个角色可以有多个权限,一个权限可以划分给多个角色

 

3、菜单表:用于在前端引导用户找到自己的权限,并可以设置多级菜单对用户权限进行划分;所以权限表和菜单表是1对多关系;

由于需要构建多级菜单,并且拥有嵌套关系,所以菜单表自引用;

启发:一般设计包含层级结构嵌套,切嵌套的层级无法预测的表结构使用自关联;(表1外键-----》表2----》外键表3是行不通的,因为无法预测嵌套层级的深度)

例如:多级评论(无法预测,评论树的深度)

三、modal.py数据模型

1、创建一个独立的app作为公共模块,以备后期遇到权限相关项目时使用;

from django.db import models

from django.db import models

class Menu(models.Model):
''' 菜单表'''
caption=models.CharField(max_length=32)
parent=models.ForeignKey('Menu',null=True,blank=True) #自关联
def __str__(self):
caption_list = [self.caption,]
p=self.parent
while p: #如果有父级菜单,一直向上寻找
caption_list.insert(0,p.caption)
p=p.parent return "-".join(caption_list) class Permission(models.Model):
'''权限表'''
title = models.CharField(max_length=64)
url = models.CharField(max_length=255)
menu = models.ForeignKey('Menu', null=True, blank=True)#和菜单是1对多关系
def __str__(self):
return '权限名称: %s--------权限所在菜单 %s'% (self.title,self.menu) class Role(models.Model):
'''角色表'''
rolename=models.CharField(max_length=32)
permission=models.ManyToManyField('Permission')
def __str__(self):
return '角色: %s--------权限 %s'% (self.rolename,self.permission) class UserInfo(models.Model):
'''用户表'''
name=models.CharField(max_length=32)
pwd=models.CharField(max_length=64)
rule=models.ManyToManyField('Role')
def __str__(self):
return self.name

四、权限初始化设置、中间件获取、判断、生成权限菜单;

当用户登录之后获取到用户名、密码查询用户表连表查询得到角色、权限信息,写入当前用户session(用session来保存用户的权限信息);

写入session之后每次用户请求到来,通过Django中间件判断用户权限;

1.用户首次登录,初始时该用户权限,写入session;

from app02 import models
from app02.service import init_session
from django.conf import settings
import re def login(reqeust):
if reqeust.method == 'GET':
return render(reqeust, 'login.html')
else:
user = reqeust.POST.get('user')
pwd = reqeust.POST.get('pwd')
user_obj = models.UserInfo.objects.filter(name=user, pwd=pwd).first()
if user:
# init_session(reqeust,user_obj)
init_session.per(reqeust,user_obj)#用户首次登录初始化用户权限信息
return redirect('/index/')
else:
return render(reqeust, 'login.html') def index(request): return HttpResponse('INDEX') def test_query(request):
return render(request,'test.html')

视图

from django.conf import settings
from .. import models
def per(reqeust,user_obj):
permission_list = user_obj.rule.values('permission__title', 'permission__url',
'permission__menu_id', ).distinct()
permission_urllist = [] # 当前用户可以访问的url(权限列表)
permission_menulist = [] # 当前用户应该挂靠到菜单上显示的权限
for iteam in permission_list:
permission_urllist.append(iteam['permission__url'])
if iteam['permission__menu_id']:
temp = {'title': iteam['permission__title'], 'url': iteam['permission__url'],
'menu_id': iteam['permission__menu_id']}
permission_menulist.append(temp)
menulist = list(models.Menu.objects.values('id', 'caption', 'parent_id')) # 获取所有菜单(以便当前用户的菜单挂靠)
from django.conf import settings
reqeust.session[settings.SESSION_PERMISSION_URL_KEY] = permission_urllist
reqeust.session[settings.SESSION_PERMISSION_MENU_URL_KEY] = {
'k1': permission_menulist,
'k2': menulist
}

init_session.per

2.用户再次登录通过Django中间件 检查当前用户session中携带的权限信息,进而判断用户是否对当前request.path有访问权限?;

from django.utils.deprecation import MiddlewareMixin
import re
from django.shortcuts import render,redirect,HttpResponse
from django.conf import settings
class Mddile1(MiddlewareMixin):
def process_request(self,request):
#如果用户访问的url是登录、注册页面,记录到白名单,放行
for url in settings.PASS_URL_LIST:
if re.match(url,request.path_info):
return None Permission_url_list=request.session.get(settings.SESSION_PERMISSION_URL_KEY)
#如果用户访问的url 不在当前用户权限之内 返回login页面
if not Permission_url_list:
return redirect(settings.LOGIN_URL)
current_url=request.path_info
#由于数据库的数据,可能是正则所有 一定要精确匹配
flag=False
for url in Permission_url_list:
url='^%s$'%(url)
if re.match(url,current_url):
flag=True
break
if not flag:
if settings.DEBUG: #如果是程序调试应该 显示用户可以访问的权限
url_html='<br/>'.join(Permission_url_list)
return HttpResponse('无权访问您可以访问%s'%url_html)
else:
return HttpResponse('没有权限') def process_response(self, request,response):
return response

五、根据用户权限生成菜单

当用户使用当前访问的通过中间件之后,要做的事情只有2步;

1、根据用户session中的权限列表,生成该用户的菜单;

2、根据用户访问的当前url,把这个菜单 从当前url(权限)从下到上展开;

def test_query(request):
menu_permission_list=request.session[settings.SESSION_PERMISSION_MENU_URL_KEY]
permission_list=menu_permission_list['k1'] #获取需要挂靠在菜单上显示的权限
menu_list=menu_permission_list['k2'] #获取全部菜单
all_menu_dict={}
# status 是用户全部权限,挂靠显示的菜单;
# open 当前url(权限)对应的父级菜单展开?
for item in menu_list:
item['child']=[]
item['status']=False
item['open']=False
all_menu_dict[item['id']]=item
current_url=request.path_info
for row in permission_list:
row['status'] = True
row['open']=False
if re.match('^%s$'% (row['url']),current_url):
row['open']=True
all_menu_dict[row['menu_id']]['child'].append(row)
pid=row['menu_id']
while pid:
all_menu_dict[pid]['status']=True
pid=all_menu_dict[pid]['parent_id']
if row['open']:
PID=row['menu_id']
while PID:
all_menu_dict[PID]['open']=True
PID=all_menu_dict[PID]['parent_id'] return HttpResponse('OK')

六、自定义模板语言 simple_tag 把用户菜单渲染到前端

from django.template import Library
from django.conf import settings
import re,os
from django.utils.safestring import mark_safe
register=Library() #生成菜单所有数据
def men_data(request):
menu_permission_list = request.session[settings.SESSION_PERMISSION_MENU_URL_KEY]
permission_list = menu_permission_list['k1'] # 获取需要挂靠在菜单上显示的权限
menu_list = menu_permission_list['k2'] # 获取全部菜单
all_menu_dict = {}
# status 是用户全部权限,挂靠显示的菜单;
# open 当前url(权限)对应的父级菜单展开?
# 把用户所有的权限挂靠到对应的菜单
for item in menu_list:
item['child'] = []
item['status'] = False
item['open'] = False
all_menu_dict[item['id']] = item
current_url = request.path_info
for row in permission_list:
row['status'] = True
row['open'] = False
if re.match('^%s$' % (row['url']), current_url):
row['open'] = True
all_menu_dict[row['menu_id']]['child'].append(row)
pid = row['menu_id']
while pid:
all_menu_dict[pid]['status'] = True
pid = all_menu_dict[pid]['parent_id']
if row['open']:
PID = row['menu_id']
while PID:
all_menu_dict[PID]['open'] = True
PID = all_menu_dict[PID]['parent_id']
# 把用户所有菜单挂父级菜单
res = []
for k, v in all_menu_dict.items():
if not v.get('parent_id'):
res.append(v)
else:
pid = v.get('parent_id')
all_menu_dict[pid]['child'].append(v)
return res #生成菜单所用HTML
def process_menu_html(menu_list):
#盛放菜单所用HTML标签
tpl1 = """
<div class='rbac-menu-item'>
<div class='rbac-menu-header'>{0}</div>
<div class='rbac-menu-body {2}'>{1}</div>
</div>
"""
#盛放权限的HTML
tpl2 = """
<a href='{0}' class='{1}'>{2}</a>
"""
html=''
for item in menu_list:
if not item['status']:
continue
else:
if item.get('url') :
# 权限
html+= tpl2.format(item['url'],'rbac_active' if item['open'] else '',item['title'])
else:
#菜单
html+= tpl1.format(item['caption'],process_menu_html(item['child']),''if item['open'] else 'rbac-hide') return mark_safe( html) @register.simple_tag
def rbac_menus(request):
res= men_data(request)
html=process_menu_html(res)
return html @register.simple_tag
def rbac_css():
file_path = os.path.join('app02', 'theme', 'rbac.css')
if os.path.exists(file_path):
return mark_safe(open(file_path, 'r', encoding='utf-8').read())
else:
raise Exception('rbac主题CSS文件不存在') @register.simple_tag
def rbac_js():
file_path = os.path.join('app02', 'theme', 'rbac.js')
if os.path.exists(file_path):
return mark_safe(open(file_path, 'r', encoding='utf-8').read())
else:
raise Exception('rbac主题JavaScript文件不存在')

七、使用 ModelForm组件 填充插件中数据

1、 Modal Form插件的简单使用

Modal Form 顾名思义 就是把Modal和Form验证的功能紧密集合起来,实现对数据库数据的增加、编辑操作;

添加

from app02 import models
from django.forms import ModelForm
class UserModalForm(ModelForm):
class Meta:
model=models.UserInfo #(该字段必须为 model 数据库中表)
fields= '__all__' #(该字段必须为 fields 数据库中表) def add(request):
# 实例化models_form
if request.method=='GET':
obj = UserModalForm()
return render(request,'rbac/user_add.html',locals())
else:
obj=UserModalForm(request.POST)
if obj.is_valid():
data=obj.cleaned_data
obj.save() #form验证通过直接 添加用户信息到数据库
return render(request, 'rbac/user_add.html', locals())

使用

def user_edit(request):
pk = request.GET.get('id')
user_obj = models.UserInfo.objects.filter(id=pk).first()
if request.method=='GET':
if not user_obj:
return redirect('/app02/user_edit/')
else:
#在form表单中自动填充默认值
model_form_obj=UserModalForm(instance=user_obj)
return render(request,'rbac/user_edit.html',locals())
else:
#修改数据 需要instance=user_obj
model_form_obj = UserModalForm(request.POST,instance=user_obj)
if model_form_obj.is_valid():
model_form_obj.save()
return redirect('/app02/userinfo/')

2、Modal Form 参数设置

from django.shortcuts import render,HttpResponse,redirect
from app02 import models
from django.forms import ModelForm
from django.forms import widgets as wid
from django.forms import fields as fid class UserModalForm(ModelForm):
class Meta:
model=models.UserInfo #(该字段必须为 model 数据库中表)
fields= '__all__' #(该字段必须为 fields '__all__',显示数据库中所有字段,
# fields=['指定字段']
# exclude=['排除指定字段'] )
# fields=['name',]
# exclude=['pwd']
#error_messages 自定制错误信息
error_messages={'name':{'required':'用户名不能为空'},
'pwd': {'required': '密码不能为空'},
} #widgets 自定制插件
# widgets={'name':wid.Textarea(attrs={'class':'c2'})}
#由于数据库里的字段 和前端显示的会有差异,可以使用 labels 定制前端显示
labels={'name':'姓名','pwd':'密码','rule':'角色'}
#自定制 input标签 输入信息提示
help_texts={'name':'别瞎写,瞎写打你哦!'}
#自定制自己 form 字段.CharField() email()等
field_classes={
'name':fid.CharField
}

3、添加数据库之外的字段,实时数据更新

ModelForm 可以结合Model把所有数据库字段在页面上生成,也可以增加额外的字段;

规则:如果增加的字段和数据里的filed重名则覆盖,不重名则新增;

也可以通过重写__init__ ,每次实例化1个form对象,实时更新数据;

class PermissionModelForm(ModelForm):
#ModelForm 可以结合Model把所有数据库字段在页面上生成,也可以增加额外的字段;
url=fields.ChoiceField()
class Meta:
fields = "__all__"
model = models.Permission #注意不是models
def __init__(self,*args,**kwargs): #重写父类的 __init__方法,每次实例化实时更新 form中的数据
super(PermissionModelForm,self).__init__(*args,**kwargs)
from pro_crm.urls import urlpatterns
self.fields['url'].choices=get_all_url(urlpatterns,'/', True)

八、总结

如何把权限精确到按钮,按钮就是子菜单就是一个url

权限管理的思路是

把用户权限记录到数据库里面

当用户首次登录时,从数据库里取出数据把用户的权限(url)和挂靠的菜单菜单/写入到session中

以后每次访问在中间件进行check;

难度在于:多级菜单之间的拼接挂靠会用到递归,所以我选择了二级菜单;

参考

Django之权限管理插件的更多相关文章

  1. mac以及centos下安装Elasticsearch 以及权限管理插件

    Elasticsearch安装(提前系统需要安装java环境)mac安装 brew install elasticsearch centos安装 下载ElasticSearch安装包,https:// ...

  2. JFinal的Shiro权限管理插件--玛雅牛 / JFinalShiro

    http://git.oschina.net/myaniu/jfinalshiroplugin JFinalShiroPlugin JFinal的Shiro插件,实现权限管理. 升级说明 1)支持JF ...

  3. 树型权限管理插件:jQuery Tree Multiselect详细使用指南

    1.认识jQuery Tree Multiselect 这个插件允许用户以树型的形式来呈现列表复选框的选择.多用于权限管理中用于分配不同的权限.使用文档,请参考:     https://github ...

  4. Django之权限管理

    Django权限管理之初步完整版 项目背景:这是一个权限管理系统(给一些角色和他们的权限指URL和页面可以删除的按钮比如:增删改查) 使用到了中间件,和初始化权限,使用了admin的后台管理系统. 我 ...

  5. Django框架----权限管理(设计分析以及具体细节)

    说起权限我们大家都知道,不一样的角色会有不一样的权限.比如就像学生管理系统一样,管理员,老师,学生之间的权限都是不一样的,那么展示的页面也是不一样的.所以,我们现在来看看具体操作. 目标:生成一个独立 ...

  6. jenkins权限管理插件role-based(二)

    一.安装role-based插件 jenkins默认自带“configure global security/全局安全配置”比较简陋,不建议使用 系统管理-->插件管理-->可选插件--& ...

  7. python 全栈开发,Day110(django ModelForm,客户管理之 编辑权限(一))

    昨日内容回顾 1. 简述权限管理的实现原理. 粒度控制到按钮级别的权限控制 - 用户登陆成功之后,将权限和菜单信息放入session - 每次请求时,在中间件中做权限校验 - inclusion_ta ...

  8. Django 权限管理(二)

    权限菜单展示 1.展示的效果 实现该效果,涉及到的核心点(突破点) 1. 权限分类: (1)菜单权限 (主要用来展示的权限(url)如查看权限 url,  如上图中的每个权限都归类为菜单权限,主要用来 ...

  9. [Django]用户权限学习系列之权限管理界面实现

    本系列前三章: http://www.cnblogs.com/CQ-LQJ/p/5604331.htmlPermission权限基本操作指令 http://www.cnblogs.com/CQ-LQJ ...

随机推荐

  1. pc网页中嵌入百度地图

    pc网页中嵌入百度地图 1 打开百度地图生成器: http://api.map.baidu.com/lbsapi/creatmap/ 2 设置好了之后,点击获取代码,将代码粘贴到文件中保存为html文 ...

  2. 【BZOJ】1875: [SDOI2009]HH去散步

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1875 注意的是路径不可以重复,所以这题把边看成点.每一条无向边拆成两条有向边. 令${F[ ...

  3. win10上安装keras

    下载Anaconda https://www.anaconda.com/ 点击进入下载界面 选择Windows版本64位,python3.7 下载完成后 ,双击安装 等待安装完成! 安装MinGW包, ...

  4. 一: vue的基本使用

    一: vue的下载 vue.js是目前前端web开发最流行的工具库之一,由尤雨溪在2014年2月发布的. 另外几个常见的工具库:react.js /angular.js 官方网站: ​ 中文:http ...

  5. python类与类的关系

    类与类之间的关系(依赖关系,大象与冰箱是依赖关系) class DaXiang: def open(self, bx): # 这里是依赖关系. 想执行这个动作. 必须传递一个bx print(&quo ...

  6. CentOS6.5下搭建LAMP+FreeRadius+Daloradius Web管理和TP-LINK路由器、H3C交换机连接,实现,上网认证和记账功能

    什么是RADIUS服务: RADIUS:(Remote Authentication Dial In User Service)中文名为远程用户拨号认证服务,简称RADIUS,是目前应用最广泛的AAA ...

  7. ubuntu16系统磁盘空间/dev/vda1占用满的问题

    参考文档: https://www.cnblogs.com/moonandstar08/p/6091507.html (系统磁盘空间/dev/xvda1占满原因分析) https://blog.csd ...

  8. 数据库锁机制(以MySQL为例)

    选自:https://blog.csdn.net/aluomaidi/article/details/52460844 https://www.zhihu.com/question/51513268/ ...

  9. Linux环境下配置及启动Hadoop(伪集群)

    1.下载tag软件包后,我习惯放到software文件夹下,并建立app文件夹2.通过tar -zxvf hadoop-2.6.0-cdh5.7.0.tar.gz -C ~/app/ 命令解压到app ...

  10. 批量显示QC结果的利器(转)

    作者:greenhillman MultiQC homepage: http://multiqc.info功能:把多个测序结果的qc结果整合成一个报告.支持fastqc.trimmomatic.bow ...