django用户权限管理(Django 内置权限扩展案例详解)
django用户权限管理
Django 内置权限扩展案例详解当django的内置权限无法满足需求的时候就自己扩展吧~
背景介绍
overmind项目使用了django内置的权限系统,django内置权限系统基于model层做控制,新的model创建后会默认新建三个权限,分别为:add、change、delete,如果给用户或组赋予delete的权限,那么用户将可以删除这个model下的所有数据。
原本overmind只管理了我们自己部门的数据库,权限设置只针对具体的功能不针对细粒度的数据库实例,例如用户a 有审核的权限,那么用户a 可以审核所有的db,此时使用内置的权限系统就可以满足需求了,但随着系统的不断完善要接入其他部门的数据库管理,这就要求针对不同用户开放不同db的权限了,例如a部门的用户只能操作a部门的db,django内置基于model的权限无法满足需求了。
实现过程
先来确定下需求:
1. 保持原本的基于功能的权限控制不变,例如用户a有查询权限,b有审核权限
2. 增加针对db实例的权限控制,例如用户a只能查询特定的db,b只能审核特定的db
对于上边需求1用内置的权限系统已经可以实现,这里不赘述,重点看下需求2,db信息都存放在同一个表里,不同用户能操作不同的db,也就是需要把每一条db信息与有权限操作的用户进行关联,为了方便操作,我们考虑把db跟用户组关联,在用户组里的用户都有权限,而操作类型经过分析主要有两类读和写,那么需要给每个mysql实例添加两个字段分别记录对此实例有读和写权限的用户组
如下代码在原来的model基础上添加 read_groups
和 write_groups
字段,db实例跟用户组应是manytomanyfield多对多关系,一个实例可以关联多个用户组,一个用户组也可以属于多个实例
|
class mysql(models.model): env = ( ( 1 , 'dev' ), ( 2 , 'qa' ), ( 3 , 'prod' ), ) create_time = models.datetimefield(auto_now_add = true, verbose_name = '创建时间' ) update_time = models.datetimefield(auto_now = true, verbose_name = '更新时间' ) project_id = models.integerfield(verbose_name = '项目' ) project_tmp = models.charfield(max_length = 128 , default = '') environment = models.integerfield(choices = env, verbose_name = '环境' ) master_host = models.genericipaddressfield(verbose_name = 'master主机' ) master_port = models.integerfield(default = 3306 , verbose_name = 'master端口' ) slave_host = models.genericipaddressfield(null = true, verbose_name = 'slave主机' ) slave_port = models.integerfield(null = true, default = 3306 , verbose_name = 'slave端口' ) database = models.charfield(max_length = 64 , verbose_name = '数据库' ) read_groups = models.manytomanyfield(group, related_name = 'read' , verbose_name = '读权限' ) write_groups = models.manytomanyfield(group, related_name = 'write' , verbose_name = '写权限' ) description = models.textfield(null = true, verbose_name = '备注' ) |
model确定了,接下来我们分三部分详细介绍下权限验证的具体实现
列表页权限控制
如上图列表页,每个用户进入系统后只能查看自己有读权限的mysql实例列表,管理员能查看所有,代码如下:
|
def mysql(request): if request.method = = 'get' : if request.user.is_superuser: _lists = mysql.objects. all ().order_by( 'id' ) else : # 获取登录用户的所有组 _user_groups = request.user.groups. all () # 构造一个空的queryset然后合并 _lists = mysql.objects.none() for group in _user_groups: _lists = _lists | group.read. all () return render(request, 'overmind/mysql.index.html' , { 'request' : request, 'lpage' : _lists}) |
实现的思路是:获取登录用户的所有组,然后循环查询每个组有读取权限的数据库实例,最后把每个组有权限读的数据库实例进行合并返回
获取登录用户的所有组用到了manytomany的查询方法: request.user.groups.all()
最终返回的一个结果是queryset,所以我们需要先构造一个空的queryset: mysql.objects.none()
queryset合并不能用简单的相加,应为: queryset-1 | queryset-2
查询接口权限控制
如上图系统中有很多功能是需要根据项目、环境查询对应的db信息的,对于此类接口也需要控制用户只能查询自己有权限读的db实例,管理员能查看所有,代码如下:
|
def get_project_database(request, project, environment): if request.method = = 'get' : _jsondata = {} if request.user.is_superuser: # 返回所有项目和环境匹配的db _lists = mysql.objects. filter ( project_id = int (project), environment = int (environment) ) _jsondata = {i. id : i.database for i in _lists} else : # 只返回用户有权限查询的db _user_groups = request.user.groups. all () for group in _user_groups: # 循环mysql表中有read_groups权限的所有组 for mysql in group.read. all (): if mysql.project_id = = int (project) and mysql.environment = = int (environment): _jsondata[mysql. id ] = mysql.database return jsonresponse(_jsondata) |
实现思路与上边类似,只是多了一步根据项目和环境再进行判断
需要根据group去反查都有哪些db实例包含了该组,这里用到了m2m的related_name属性: group.read.all()
更多关于django orm查询的内容可以看这篇文章 django model select的各种用法详解 有详细的总结
执行操作权限控制
除了上边的两个场景之外我们还需要在执行具体的操作之前去判断是否有权限,例如执行审核操作前判断用户是否对此db有写权限
有很多地方都需要做这个判断,所以把这个权限判断单独写个方法来处理,代码如下:
|
def check_permission(perm, mysql, user): # 如果用户是超级管理员则有权限 if user.is_superuser: return true # 取出用户所属的所有组 _user_groups = user.groups. all () # 取出mysql对应权限的所有组 if perm = = 'read' : _mysql_groups = mysql.read_groups. all () if perm = = 'write' : _mysql_groups = mysql.write_groups. all () # 用户组和db权限组取交集,有则表示有权限,否则没有权限 group_list = list ( set (_user_groups).intersection( set (_mysql_groups))) return false if len (group_list) = = 0 else true |
实现思路是:根据传入的第三个用户参数,来获取到用户所有的组,然后根据传入的第一个参数类型读取或写入和第二个参数db实例来获取到有权限的所有组,然后对两个组取交集,交集不为空则表示有权限,为空则没有
m2m的 .all()
取出来的结果是个list,两个list取交集的方法为: list(set(list-a).intersection(set(list-b)))
view中使用就很简单了,如下:
|
def query(request): if request.method = = 'post' : postdata = request.body.decode( 'utf-8' ) _host = get_object_or_404(mysql, id = int (postdata.get( 'database' ))) # 检查用户是否有db的查询权限 if check_permission( 'read' , _host, request.user) = = false: return jsonresponse({ 'state' : 0 , 'message' : '当前用户没有查询此db的权限' }) |
写在最后
1. django有第三方的基于object的权限管理模块django-guardian,本项目没有使用主要是因为一来权限需求并不复杂,自己实现也很方便,二来个人在非必要的情况下并不喜欢引用过多第三方的包,后续升级维护都是负担
2. 方案和代码不尽完美,各位有更好的方案建议或更优雅的代码写法欢迎与我交流
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持开心学习网。
原文链接:https://mp.weixin.qq.com/s?__biz=MzU5MDY1MzcyOQ==&mid=2247483944&idx=1&sn=fed70b8c31b7a7a3dc2ae1d9f2d0596f&scene=21#wechat_redirect
- php图片合成处理(PHP使用 Imagick 扩展实现图片合成,圆角处理功能示例)
- dedecms中的有些功能如何修改(DEDECMS 扩展标签和dede自定义标签实现方法)
- 宝塔面板与php(宝塔面板如何安装PHP扩展)
- php如何继承多个类(PHP面向对象程序设计子类扩展父类子类重新载入父类操作详解)
- vue router用法(如何在Vue 3中扩展Vue Router链接详解)
- javascript数组实例扩展方法(JavaScript如何监测数组的变化)
- amaze算法(amazeui 验证按钮扩展的实现)
- C# this扩展方法
- phpmysql怎么搭建(PHP使用PDO、mysqli扩展实现与数据库交互操作详解)
- cubeide调试问题(如何使用宝塔安装ionCube扩展)
- 宝塔面板扩展安装(宝塔面板一键安装配置sg11组件教程)
- 解忧大队es6扩展运算符(ES6扩展运算符的使用方法示例)
- python3循环使用教程(Python3.4学习笔记之 idle 清屏扩展插件用法分析)
- C# 扩展方法
- php7处理方案(PHP7 安装event扩展的实现方法)
- php扩展库使用教程(php使用pecl方式安装扩展操作示例)
- 如何看待美国数十万加仑牛奶倒下水道 历史又重演了(如何看待美国数十万加仑牛奶倒下水道)
- 历史惊人的相似,美国80万加仑牛奶倒入下水道,意味着什么(历史惊人的相似)
- 美国数十万加仑牛奶倒进下水道,世界会重演1929年的大萧条吗(美国数十万加仑牛奶倒进下水道)
- 美国数十万加仑牛奶倒入下水道,贫民区食不果腹,历史再次重演(美国数十万加仑牛奶倒入下水道)
- 美国倒掉数十万加仑牛奶 上热搜第一,这一幕似曾相识(美国倒掉数十万加仑牛奶)
- 深度 倒牛奶 这一幕为何又在美国上演(深度倒牛奶)
热门推荐
- 织梦后台参数怎么设置才符合seo(详解织梦模板自定义表单限制IP24小时只能提交一次教程)
- dedecms的简单说明(DEDECMS5.5在国外Linux主机下无法采集的解决方法)
- yii框架使用教程(Yii框架分页技术实例分析)
- docker怎么装mongodb(Docker 搭建集群MongoDB的实现步骤)
- pythonshell入门教程(python获取交互式ssh shell的方法)
- 详解DB2 sqlstate 57016 SQLCODE=-668 原因码 "7"错误的快速解决办法(详解DB2 sqlstate 57016 SQLCODE=-668 原因码 "7"错误的快速解决办法)
- css3弹性盒模型常用属性(CSS3弹性盒模型开发笔记三)
- dedecms 操作日志代码修改(dedecms 调用单页栏目内容到首页的方法)
- win7iis服务器的安装与配置(用IIS建立高安全性Web服务器的方法)
- SQL Server 批量导入数据的方法
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9