当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_groupswrite_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.databasereturn 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. 方案和代码不尽完美,各位有更好的方案建议或更优雅的代码写法欢迎与我交流

相关文章推荐阅读:

  • Django+JWT实现Token认证
  • 我们自研的那些Devops工具

转载于:https://www.cnblogs.com/37Y37/p/10514575.html

Django内置权限扩展案例相关推荐

  1. Django内置分页扩展

    url文件 urlpatterns = [path('admin/', admin.site.urls),path('index1.html/', views.index1), ] views文件 f ...

  2. Django内置Admin

    Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有: 依赖APP:django.contrib.authdjango.contrib.contenttypes ...

  3. ES6新增语法与内置对象扩展

    技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1.什么是ES6 ES的全称是ECMAScript,它是由ECMA国际标准化组织制定的一项 ...

  4. django 内置 admin

    Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件使用方式有依赖APPdjango.contrib.authdjango.contrib.contenttypesdjan ...

  5. 〖Python〗-- Django内置Admin

    [Django内置Admin] Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有: 依赖APP:django.contrib.authdjango.cont ...

  6. Django - 内置admin

    Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有: Django内置的Admin是对于model中对应的数据表进行增删改查提供的组件,使用方式有:复制代码依 ...

  7. es6 内置对象扩展rest, Arry 扩展方法Array.from(),find(), findIndex(),includes()

    1.es6内置对象扩展rest实参 2.  Arry 扩展方法    (1)构造函数方法:Array.from()   (2) find()   (3) findIndex()   (4)includ ...

  8. Django内置的用户认证

    认证登陆 在进行用户登陆验证的时候,如果是自己写代码,就必须要先查询数据库,看用户输入的用户名是否存在于数据库中: 如果用户存在于数据库中,然后再验证用户输入的密码,这样一来就要自己编写大量的代码. ...

  9. django 内置标签与过滤器

    #内置标签与过滤器 本文档介绍了Django的内置模板标签和过滤器. 我们推荐尽可能使用 自动文档,同时也可以自行编辑任何已安装的自定义标签或过滤器的文档. ##内置标记引用 ###autoescap ...

最新文章

  1. python基础--函数
  2. Elmah 日志记录组件
  3. net.sf.json.JSONObject 和org.json.JSONObject 的差别
  4. mysql隔离级别与悲观锁、乐观锁
  5. DBSCAN密度聚类
  6. html透明度_学好Web前端开发,必要了解的HTML+CSS的技巧有哪些
  7. 计算机组装与维护补考论文,探讨《计算机组装与维护》课程教学论文
  8. ft2232驱动安装方法_ST-Link资料03_ST-Link固件升级、驱动下载安装方法
  9. mysql查看导入大小_mysql 数据导入、导出,及库大小查看
  10. OpenSSL再曝CCS注入漏洞-心伤未愈又成筛子
  11. HTTP状态码及含义
  12. webpack模块化原理-ES module
  13. 扩展linux swap分区大小,扩展Linux系统swap分区的大小
  14. ubuntu 如何编译 java_在ubuntu中编译运行java程序
  15. oracle backup exec,通过Backup Exec实施Oracle灾难恢复
  16. 广数系统加工中心编程_CNC数控加工中心编程指令详解
  17. 正则表达式匹配以xx开头以xx结尾
  18. 黑马程序员--IO总结(含2个设计模式)
  19. STM8S003国产替代 DP32G003 32 位微控制器芯片
  20. 微型计算机一个汉字多少字节,一个汉字多少字节(Byte)?

热门文章

  1. PWN-PRACTICE-BUUCTF-14
  2. 【POJ - 1459】Power Network(网络流最大流,建图)
  3. 【HDU - 5963】朋友(博弈,思维,必胜态必败态,找规律)
  4. 【POJ - 2553】The Bottom of a Graph(tarjan强连通分量缩点,模板题)
  5. *【POJ - 2796】 Feel Good (前缀和优化+单调栈维护)
  6. 【计蒜客 - 2019南昌邀请赛网络赛 - H】Coloring Game(找规律,思维dp)
  7. 【POJ - 2785】4 Values whose Sum is 0 (二分,折半枚举)
  8. 【qduoj - 纳新题】凑数题(恰好装满类0-1背包 或 母函数)
  9. Coursera自动驾驶课程第17讲:An Autonomous Vehicle State Estimator
  10. RabbitMQ初识