Django权限机制实现代码详解


Posted in Python onFebruary 05, 2018

本文研究的主要是Django权限机制的相关内容,具体如下。

1. Django权限机制概述

权限机制能够约束用户行为,控制页面的显示内容,也能使API更加安全和灵活;用好权限机制,能让系统更加强大和健壮。因此,基于Django的开发,理清Django权限机制是非常必要的。

1.1 Django的权限控制

Django用user, group和permission完成了权限机制,这个权限机制是将属于model的某个permission赋予user或group,可以理解为全局的权限,即如果用户A对数据模型(model)B有可写权限,那么A能修改model B的所有实例(objects)。group的权限也是如此,如果为group C 赋予model B的可写权限,则隶属于group C 的所有用户,都可以修改model B的所有实例。

这种权限机制只能解决一些简单的应用需求,而大部分应用场景下,需要更细分的权限机制。以博客系统为例,博客系统的用户可分为『管理员』、『编辑』、『作者』和『读者』四个用户组;博客系统管理员和编辑具有查看、修改和删除所有的文章的权限,作者只能修改和删除自己写的文章,而读者则只有阅读权限。管理员、编辑和读者的权限,我们可以用全局权限做控制,而对于作者,全局权限无法满足需求,仅通过全局权限,要么允许作者编辑不属于自己的文章,要么让作者连自己的文章都无法修改。

上述的应用场景,Django自带的权限机制无法满足需求,需要引入另一种更细的权限机制:对象权限(object permission)。

Object Permission是一种对象颗粒度上的权限机制,它允许为每个具体对象授权。仍沿用最开始的例子,如果model B有三个实例 B1,B2 和B3,如果我们把B1的可写权限赋予用户A,则A可以修改B1对象,而对B2,B3无法修改。

对group也一样,如果将B2的可写权限赋予group C,则隶属于group C的所有用户均可以修改B2,但无法修改B1和B3。结合Django自带权限机制和object permission,博客系统中作者的权限控制迎刃而解:系统全局上不允许作者编辑文章,而对于属于作者的具体文章,赋予编辑权限即可。

Django其实包含了object permission的框架,但没有具体实现,object permission的实现需要借助第三方app django-guardian,我们在开发中用调用django guradian封装好的方法即可。

1.2 Django的权限项

Django用permission对象存储权限项,每个model默认都有三个permission,即add model, change model和delete model。例如,定义一个名为『Car』model,定义好Car之后,会自动创建相应的三个permission:add_car, change_car和delete_car。Django还允许自定义permission,例如,我们可以为Car创建新的权限项:drive_car, clean_car, fix_car等等

需要注意的是,permission总是与model对应的,如果一个object不是model的实例,我们无法为它创建/分配权限。

2. Django 自带权限机制的应用

2.1 Permission

如上文所述,Django定义每个model后,默认都会添加该model的add, change和delete三个permission,自定义的permission可以在我们定义model时手动添加:

class Task(models.Model):
  ...
  class Meta:
    permissions = (
      ("view_task", "Can see available tasks"),
      ("change_task_status", "Can change the status of tasks"),
      ("close_task", "Can remove a task by setting its status as closed"),
    )

每个permission都是django.contrib.auth.Permission类型的实例,该类型包含三个字段name, codename 和 content_type,其中 content_type反应了permission属于哪个model,codename如上面的view_task,代码逻辑中检查权限时要用, name是permission的描述,将permission打印到屏幕或页面时默认显示的就是name。

在model中创建自定义权限,从系统开发的角度,可理解为创建系统的内置权限,如果需求中涉及到用户使用系统时创建自定义权限,则要通过下面方法:

from myapp.models import BlogPost
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.create(codename='can_publish',
                    name='Can Publish Posts',
                    content_type=content_type)

2.2 User Permission管理

User对象的user_permission字段管理用户的权限:

myuser.user_permissions = [permission_list]
myuser.user_permissions.add(permission, permission, ...) #增加权限
myuser.user_permissions.remove(permission, permission, ...) #删除权限
myuser.user_permissions.clear() #清空权限

##############################################################
# 注:上面的permission为django.contrib.auth.Permission类型的实例
##############################################################

检查用户权限用has_perm()方法:

myuser.has_perm('myapp.fix_car')

has_perm()方法的参数,即permission的codename,但传递参数时需要加上model 所属app的前缀,格式为<app label>.<permission codename>

无论permission赋予user还是group,has_perm()方法均适用

附注:
user.get_all_permissions()方法列出用户的所有权限,返回值是permission name的list
user.get_group_permissions()方法列出用户所属group的权限,返回值是permission name的list

2.3 Group Permission管理

group permission管理逻辑与user permission管理一致,group中使用permissions字段做权限管理:

group.permissions = [permission_list]
group.permissions.add(permission, permission, ...)
group.permissions.remove(permission, permission, ...)
group.permissions.clear()

权限检查:

依然使用user.has_perm()方法。

2.4 permission_required 装饰器

权限能约束用户行为,当业务逻辑中涉及到权限检查时,decorator能够分离权限验证和核心的业务逻辑,使代码更简洁,逻辑更清晰。permission的decorator为permission_required

from django.contrib.auth.decorators import permission_required

@permission_required('car.drive_car')
def my_view(request):

2.5 Template中的权限检查

Template中使用全局变量perms存储当前用户的所有权限,权限检查可以参考下面例子:

{% if perms.main.add_page %}
      <li class="dropdown">
       <a href="#" rel="external nofollow" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Pages <span class="caret"></span></a>
       <ul class="dropdown-menu" role="menu">
        <li><a href="{% url 'main:admin_pages' %}" rel="external nofollow" >All Pages</a></li>
        <li><a href="{% url 'main:admin_page' %}" rel="external nofollow" >New Page</a></li>
        <li><a href="{% url 'main:admin_pages' %}?draft=true" rel="external nofollow" >Drafts</a></li>
       </ul>
      </li>
{% endif %}

3. 基于Django-guardian的object permission的应用

Django-guardian基于django的原生逻辑扩展了django的权限机制,应用django-guardian后,可以使用django-guardian提供的方法以及django的原生方法检查全局权限,django-guardian提供的object permission机制使django的权限机制更加完善。

django-guardian详细的使用文档请参考官方文档,其object permission常用方法如下:

from guardian.shortcuts import assign_perm, get_perms
from guardian.core import ObjectPermissionChecker
from guardian.decorators import permission_required

3.1 添加object permission

添加object permission使用assign_perm()方法,如为用户添加对mycar对象的drive_car权限:

assign_perm('myapp.drive_car', request.user, mycar)

assign_perm()方法也可用于group

assign_perm('myapp.drive_car', mygroup, mycar)

3.2 权限检查

3.2.1 Global permission

get_perms()方法用于检查用户的“全局权限”(global permission),与user.has_perm()异曲同工,如:

#############################
# It works! 
#############################
 if not 'main.change_post' in get_perms(request.user, post):
   raise HttpResponse('Forbidden')

#############################
# It works, too!
#############################
if not request.user.has_perm('main.change_post')
  return HttpResponse('Forbidden')

例子中虽然把post object作为参数传给get_perms()方法,但它只检查user的全局权限中是否有main.change_post权限,很多情况下可用原生的user.has_perm取代,但user和group均可作为get_perms()的传入参数,某些情况下可以使代码更简洁。

3.2.2 Object permission

Django-guardian中使用ObjectPermissionChecker检查用户的object permission,示例如下:

checker = ObjectPermissionChecker(request.user)
print checker.has_perm('main.change_post', post)

3.3 permission_required装饰器

guardian.decorators.permission_required是django-guardian权限检查的decorator,既可以检查全局权限,又可以检查对象权限(object permission),其中,accept_global_perms参数指出是否检查user的global permission,如:

from guardian.decorators import permission_required

class DeletePost(View):
  @method_decorator(permission_required('main.delete_post', 
              (models.Post, 'id', 'pk'), 
              accept_global_perms=True))
  def get(self, request, pk):
    try:
      pk = int(pk)
      cur_post = models.Post.objects.get(pk=pk)
      is_draft = cur_post.is_draft

      url = reverse('main:admin_posts')
      if is_draft:
        url = '{0}?draft=true'.format(url)  
      cur_post.delete()
    except models.Post.DoesNotExist:
      raise Http404

    return redirect(url)

注:
decorator中的(models.Post, ‘id', ‘pk')部分,用于指定object实例,如果忽略这个参数,则不论accept_global_perms值为True还是False,均仅仅检查全局权限。

4. 结论

Django原生提供了一种简单的全局权限(global permission)控制机制,但很多应用场景下,对象权限(object permission)更加有用;django-guardian是目前比较活跃的一个django extension,提供了一种有效的object permission控制机制,与django原生机制一脉相承,推荐使用。

以上就是本文关于Django权限机制实现代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

Python 相关文章推荐
python 从远程服务器下载日志文件的程序
Feb 10 Python
Python获取暗黑破坏神3战网前1000命位玩家的英雄技能统计
Jul 04 Python
基于python3 类的属性、方法、封装、继承实例讲解
Sep 19 Python
Python实现获取nginx服务器ip及流量统计信息功能示例
May 18 Python
Python之csv文件从MySQL数据库导入导出的方法
Jun 21 Python
python爬虫简单的添加代理进行访问的实现代码
Apr 04 Python
如何基于Python实现电子邮件的发送
Dec 16 Python
Python面向对象之继承原理与用法案例分析
Dec 31 Python
Python自动化办公Excel模块openpyxl原理及用法解析
Nov 05 Python
利用python 下载bilibili视频
Nov 13 Python
python上下文管理的使用场景实例讲解
Mar 03 Python
Python基础之进程详解
May 21 Python
Django中的Signal代码详解
Feb 05 #Python
Python实现XML文件解析的示例代码
Feb 05 #Python
Python下载网络文本数据到本地内存的四种实现方法示例
Feb 05 #Python
Python实现屏幕截图的两种方式
Feb 05 #Python
Python实现连接两个无规则列表后删除重复元素并升序排序的方法
Feb 05 #Python
用python实现对比两张图片的不同
Feb 05 #Python
使用pygame模块编写贪吃蛇的实例讲解
Feb 05 #Python
You might like
php下防止单引号,双引号在接受页面转义的设置方法
2008/09/25 PHP
windows下升级PHP到5.3.3的过程及注意事项
2010/10/12 PHP
php 大数据量及海量数据处理算法总结
2011/05/07 PHP
php用ini_get获取php.ini里变量值的方法
2015/03/04 PHP
php通过正则表达式记取数据来读取xml的方法
2015/03/09 PHP
php实现将任意进制数转换成10进制的方法
2015/04/17 PHP
php实现概率性随机抽奖代码
2016/01/02 PHP
简单实用的PHP文本缓存类实例
2019/03/22 PHP
thinkPHP3.2使用RBAC实现权限管理的实现
2019/08/27 PHP
js锁屏解屏通过对$.ajax进行封装实现
2014/07/31 Javascript
Javascript闭包用法实例分析
2015/01/23 Javascript
jquery获取select,option所有的value和text的实例
2017/03/06 Javascript
JavaScript+HTML5实现的日期比较功能示例
2017/07/12 Javascript
jQuery Easyui Treegrid实现显示checkbox功能
2017/08/08 jQuery
vue-cli webpack2项目打包优化分享
2018/02/07 Javascript
JS实现点击发送验证码 xx秒后重新发送功能
2019/07/30 Javascript
[42:32]完美世界DOTA2联赛PWL S2 LBZS vs FTD.C 第二场 11.27
2020/12/01 DOTA
Python深入学习之内存管理
2014/08/31 Python
python任务调度实例分析
2015/05/19 Python
六个窍门助你提高Python运行效率
2015/06/09 Python
Python自动扫雷实现方法
2015/07/25 Python
详解Python中的array数组模块相关使用
2016/07/05 Python
用Pelican搭建一个极简静态博客系统过程解析
2019/08/22 Python
pycharm显示远程图片的实现
2019/11/04 Python
PyCharm最新激活码(2020/10/27全网最新)
2020/10/27 Python
基于HTML5 Canvas 实现商场监控实例详解
2017/11/20 HTML / CSS
艺术系应届生的自我评价
2013/10/19 职场文书
关于打架的检讨书
2014/01/17 职场文书
初中家长寄语
2014/04/02 职场文书
英语课外活动总结
2014/08/27 职场文书
医学检验专业自荐信
2014/09/18 职场文书
土木工程专业本科生求职信
2014/10/01 职场文书
交通事故和解协议书
2015/01/27 职场文书
材料员岗位职责
2015/02/10 职场文书
2015年学校医务室工作总结
2015/07/20 职场文书
Nginx实现会话保持的两种方式
2022/03/18 Servers