django(权限、认证)系统——第三方组件实现Object级别权限控制

在我的系列blog《Django中内置的权限控制》中明确提及到,Django默认并没有提供对Object级别的权限控制,而只是在架构上留了口子。在这篇blog中,我们探讨一个简单流行的Django组件django-guardian来实现Object level permission。

安装配置django-guardian

首先需要安装django-guardian,一般我们喜欢用virtualenv创建一个虚拟环境:

>>virtualenv --distribute venv

>>source venv/bin/activate

>>pip install Django

>>pip install django-guardian

 这样,我们需要的django-guardian 就安装好了。

接下来我们需要让Django知道它,在INSTALLED_APPS变量中加入guardian:

INSTALLED_APPS = (

    ‘guardian‘,

)

然后,如果仔细读过《Django中内置的权限控制》的第五篇文章的读者,应该猜到,我们需要添加一个backend到AUTHENTICATION_BACKENDS中,这样django才会具有对对象的权限控制:

AUTHENTICATION_BACKENDS = (

    ‘django.contrib.auth.backends.ModelBackend‘# django默认的backend

    ‘guardian.backends.ObjectPermissionBackend‘,

)

在guardian中,还支持对匿名用户AnoymousUser的Object级别的权限控制,这种需求很常见,比方说允许匿名发言的论坛或者blog系统。要做到这一点需要在settings中加入:

ANONYMOUS_USER_ID=-1

接下来我们执行python manage syncdb.执行完毕之后,系统将会创建一个User实例,叫做AnonymouseUser。

完成了以上几点,guardian就被安装好了,可以开始使用了。

设置和使用对象权限:

首先当然是设置和使用对象权限了,guardian提供了一个简单的方法:

guardian.shortcuts.assign(perm, user_or_group, obj=None),这个方法接受3个参数:

  1. perm,这个参数是一个字符串,代表一个许可,格式必须为<app>.<perm_codename>或者<perm_codename>。但是如果第三个参数是None,则必须为<app>.<perm_codename>格式。因此建议还是统一使用<app>.<perm_codename>格式。注意app并不是app的全路径,而是最后一级的模块名。这一点和INSTALL_APP中的app全路径不同,如果你的app module不只一级的话,这地方一定要注意。
  2. user_or_group,这个参数是一个User或者Group类型的对象。
  3. obj,这个参数就是相关的对象了。改参数是可省略的,如果省略则赋予Model权限。

通过这个方法我们可以很方便通过传入一个<app>.<perm_codename>格式的字符串来给用户User或组Group赋予权限了。如果不传入第三个参数,则可以当作User.user_permissions.add(permissioninstance) 的快捷方式。

下面是赋予模型级别的权限:

from guardian.shortcuts import assign

user = User.objects.create(username=‘liuyong‘)

assign(‘app.view_task‘, user)

user.has_perm(‘app.view_task‘) >>True

注意,一旦赋予模型级的权限,那么所有该模型的对象级别的权限就都有了,所以应该先从对象级别进行设置,清空刚刚分配的权限然后再设置对象权限:

user = User.objects.get(username=‘liuyong‘)

user.user_permissions.clear()

task = Task.objects.create(summary=‘Some job‘, content=‘‘)

assign(‘app.view_task‘, user, task)

user = User.objects.get(username=‘liuyong‘)#刷新缓存

user.has_perm(‘app.view_task‘,task)

>>True

user.has_perm(‘app.view_task‘)#模型级别的权限还没有

>>False

我们也可以通过设置group来使用户具有相应的权限:

>>> group = Group.objects.create(name=‘employees‘)

>>> assign(‘change_task‘, group, task)

>>> user.has_perm(‘change_task‘, task)

False

>>> # user还不是employees组的成员,我们加入一下

>>> user.groups.add(group)

>>> user.has_perm(‘change_task‘, task)

True

接下来是删除某个用户对某个对象的某种许可,我们需要使用guardian.shortcuts模块中的remove_perm()函数。这个函数的签名和assign相同,都是三个:

guardian.shortcuts.remove_perm(perm,user_or_group=None, obj=None)

样例代码:

>>> from guardian.shortcuts import remove_perm

>>> remove_perm(‘change_site‘, user, site)

>>> user = User.objects.get(username=‘joe‘#刷新user对象缓存

>>> joe.has_perm(‘change_site‘, site)

False

  

好上面就是guadian的安装配置和基本使用方法,下面介绍在Django的View中所能使用的一些helper函数。

Guardian在View中的使用

除了Django的user.has_perm方法之外,guardian提供了一些帮助函数能让我们生活的更轻松。

guardian.shortcuts.get_perms(user_or_group,obj)

该方法返回user对象对obj对象所有的权限。这个行数接受两个参数,一个是user对象或者组对象,一个是相关的对象。

比如我们可以用:

‘permcodename‘ in get_perms(group,obj)来判断该组是否有这个权限,因为group没有has_perm方法。

guardian.shortcuts.get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=False)

该函数获得该用户下指定perm列表中的所有对象。比如我要获得某一个用户,拥有编辑权限的所有帖子。

get_objects_for_user(user,‘app.change_post‘)

>>所有可编辑的帖子

guadian.core.ObjectPermissionChecker

该方法是一个用来判断权限的包装器,针对user和group提供权限相关的访问方法,主要有has_perm(perm,obj)和get_perms(obj)两个方法。并且提供缓存机制,在多次查找权限的时候,可以使用它。

>>> epser = User.objects.get(username=‘esper‘)

>>> site = Site.objects.get_current()

>>> from guardian.core import ObjectPermissionChecker

>>> checker = ObjectPermissionChecker(esper) # 我们也可以传入组group对象

>>> checker.has_perm(‘change_site‘, site)

True

>>> checker.has_perm(‘add_site‘, site) # 这次将不会产生数据库查询

False

>>> checker.get_perms(site)

[u‘change_site‘]

使用view的decorator

我们可以使用decorator来减少我们的代码:

下面的代码,演示了通过decorator控制一个view函数的访问。我们要做到的是,只有拥有对name=foobars的Group对象拥有auth.change_group权限的用户,才能够执行这个view函数,否则返回的将是状态码为403的Response对象。

>>> joe = User.objects.get(username=‘joe‘)

>>> foobars = Group.objects.create(name=‘foobars‘)

>>>

>>> from guardian.decorators import permission_required_or_403

>>> from django.http import HttpResponse

>>>

>>> @permission_required_or_403(‘auth.change_group‘,

>>>     (Group, ‘name‘‘group_name‘))

>>> def edit_group(request, group_name):

>>>     return HttpResponse(‘some form‘)

>>>

>>> from django.http import HttpRequest

>>> request = HttpRequest()

>>> request.user = joe

>>> edit_group(request, group_name=‘foobars‘)

<django.http.HttpResponseForbidden object at 0x102b43dd0>

>>>

>>> joe.groups.add(foobars)

>>> edit_group(request, group_name=‘foobars‘)

<django.http.HttpResponseForbidden object at 0x102b43e50>

>>>

>>> from guardian.shortcuts import assign

>>> assign(‘auth.change_group‘, joe, foobars)

<UserObjectPermission: foobars | joe | change_group>

>>>

>>> edit_group(request, group_name=‘foobars‘)

<django.http.HttpResponse object at 0x102b8c8d0>

>>> # 这时,我们已经分配了权限,因此我们的view方法得以顺利访问了。

Guardian在模版中的使用

和Django一样,我们也需要在界面上进行权限控制以显示不同的界面。

Guardian提供了标签:

get_obj_perms

需要加载guardian_tags标签库,在需要使用guardian标签的模版上面,将其引用近来:

{% load guardian_tags %}

标签格式为:

{% get_obj_perms user/group for obj as "context_var" %}

例子代码如下:

{% get_obj_perms request.user for flatpage as "flatpage_perms" %}

{% if "delete_flatpage" in flatpage_perms %}

    <a href="/pages/delete?target={{ flatpage.url }}">Remove page</a>

{% endif %}

下面探讨一下稍微复杂一些的情况,关于孤儿对象许可(Orphaned object permissions):

孤儿对象许可

所谓孤儿许可,就是没用的许可。在大多数情况下,可能没啥事儿,但是一旦发生,后果有可能非常严重。

Guardian用来纪录某用户对某个模型对象有某个权限的纪录时是使用UserObjectPermission和GroupObjectPermission对象纪录的。其中对于object的引用是contenttype对象(标示是那个模型类)和pk主键,对于用户则是对User表的外键引用。

比方说,有一个对象A。我们通过权限设置,设定joe用户对该对象有着编辑权限。忽然有一天,用户joe被删除了。可想而知,我们分配而产生的UserObjectPermission对象仍然在数据库里面,记录着:joe 有对A的编辑权限。又有一天,一个用户注册了一个用户,用户username为joe。因为之前的那个纪录,joe用户拥有对A的编辑权限。而此joe非彼joe,我们犯了一个大错误!

再比如说,当我们删除了某一个对象的时候,而这个对象的某种权限已经被赋予给某个用户,那么这个权限的纪录也就失效了。如果什么时候和曾经删除过的对象是同一个模型类,而且主键和以前的那个相同,那么用户也就有可能对其本不应该拥有权限的对象有了权限。呵呵,说起来有点绕,但是应该很容易理解。

因此,当我们删除User和相关的Object的时候,我们一定要删除其相关的所有UserObjectPermission和GroupObjectPermission对象。

要解决这个办法有三个办法,一个是显式编码,一个是通过其提供的自定义django命令:

$ python manage.py clean_orphan_obj_perms

Removed 11 object permission entries with no targets

还有一个是定期调用guardian.utils.clean_orphan_obj_perms()

该函数会返回删除的对象数目。在python的世界中,我们可以使用celery定期调度这个任务。

但是自定义命令和定期调度都不是合理的生产环境的解决办法。要想真正解决,还是需要手动编码实现,最优雅的方式还是加上post_delete signal给User或Object对象,关于对象的样例代码如下:

from django.contrib.auth.models import User

from django.contrib.contenttypes.models import ContentType

from django.db.models import Q

from django.db.models.signals import pre_delete

from guardian.models import UserObjectPermission

from guardian.models import GroupObjectPermission

from school.models import StudyGroup

def remove_obj_perms_connected_with_user(sender, instance, **kwargs):

    filters = Q(content_type=ContentType.objects.get_for_model(instance),

        object_pk=instance.pk)

    UserObjectPermission.objects.filter(filters).delete()

    GroupObjectPermission.objects.filter(filters).delete()

pre_delete.connect(remove_obj_perms_connected_with_user, sender=StudyGroup)

这样就搞定了,不过需要写一些必要的代码。

时间: 2024-12-28 15:14:48

django(权限、认证)系统——第三方组件实现Object级别权限控制的相关文章

Django用户认证系统 authentication system

Django集成了用户登陆/验证/登出的模块,可以直接拿来使用,也可以进行定制,使用起来非常方便. 一,创建Django工程 $ django-admin.py startproject mysite 工程目录结构如下 [email protected]:~/Study/django$ tree mysite/ mysite/ ├── manage.py └── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py 1 di

Django用户认证系统(三)组与权限

Django的权限系统很简单,它可以赋予users或groups中的users以权限. Django admin后台就使用了该权限系统,不过也可以用到你自己的代码中. User对象具有两个ManyToManyField字段,groups和user_permissions groups = models.ManyToManyField(Group, verbose_name=_('groups'), blank=True, help_text=_('The groups this user bel

Django用户认证系统(一)User对象

User对象 User对象是认证系统的核心.用户对象通常用来代表网站的用户,并支持例如访问控制.注册用户.关联创建者和内容等.在Django认证框架中只有一个用户类,例如超级用户('superusers’)或('staff')用户只不过是相同用户对象设置了不同属性而已. 缺省字段Fields username 用户名,必需字段.30个字符或更少,可以包含 _, @, +, . 和 - 字符. first_name可选. 30 characters or fewer. last_name可选. 3

django 用户认证系统

django认证系统包含三个部分:用户.权限和分组 安装 django项目默认启用了认证系统,如果不是使用django-admin.py创建项目的可以通过在settings配置文件里面的INSTALLED_APPS的列表里面添加django.contrib.auth和django.contrib.contenttypes这两项然后运行manage.py syncdb命令创建对应的数据库表即可 用户Users 在Django-1.4.10\django\contrib\auth这个目录下有一个mo

【Django】认证系统

目录 #. auth模块 1. 认证 authenticate() 2. 登陆 login(HttpRequest, user) 3. 注销 logout(request) 4. 认证判断 is_authenticated() 5. 登陆校验 login_requierd() 6. 创建普通用户 create_user() 7. 创建超级用户 create_superuser() 8. 密码校验 check_password(password) 9. 修改密码 set_password(new_

Django用户认证系统(二)Web请求中的认证

在每个Web请求中都提供一个 request.user 属性来表示当前用户.如果当前用户未登录,则该属性为AnonymousUser的一个实例,反之,则是一个User实例. 你可以通过is_authenticated()来区分,例如: if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users. 登陆login login(

Django用户认证系统

一. 认证系统概要 create_user 创建用户 authenticate 验证登录 login 记住用户的登录状态 logout 退出登录 is_authenticated 判断用户是否登录 login_required 判断用户是否登录的装饰器 二. 创建用户和验证登录 当用户注册的时候用create_user(username,password,email)默认情况下is_active=True,is_staff=False,is_superuser=False. 底层将passwor

django用户认证系统——登录4

用户已经能够在我们的网站注册了,注册就是为了登录,接下来我们为用户提供登录功能.和注册不同的是,Django 已经为我们写好了登录功能的全部代码,我们不必像之前处理注册流程那样费劲了.只需几分钟的简单配置,就可为用户提供登录功能.接下来就来看看如何使用内置的登录功能. 引入内置的 URL 模型 Django 内置的登录.修改密码.找回密码等视图函数对应的 URL 模式位于 django.contrib.auth.urls.py 中,首先在工程的 urls.py 文件里包含这些 URL 模式.打开

[Python自学] Django的认证系统

一.Django自带的认证模块 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统. 此时我们需要实现包括用户注册.用户登录.用户认证.注销.修改密码等功能,这还真是个麻烦的事情呢. Django作为一个完美主义者的终极框架,当然也会想到用户的这些痛点. 它内置了强大的用户认证系统--auth,它默认使用 auth_user 表来存储用户数据. 原文地址:https://www.cnblogs.com/leokale-zz/p/12219504.html