zoukankan      html  css  js  c++  java
  • Django-guardian

    概述

    django-guardian是为Django提供额外的基于对象权限的身份验证后端。

    特征

    • Django的对象全新啊

    • 匿名用户的支持

    • 高级API

    • 经过严密测试

    • Django admin的整合

    • 装饰器

    安装

    要求Django1.7或更高版本

    pip install django-guardian

    easy_install django-guardian

    配置

    安装完成后,我们可以将django-guardian加入到我们的项目。首先在settings里将guardian加入到INSTALLED_APPS

    INSTALLED_APPS = ( 
    # ...
    'guardian',
    )

    然后加入到身份验证后端AUTHENTICATION_BACKENDS

    AUTHENTICATION_BACKENDS = ( 
    'django.contrib.auth.backends.ModelBackend', # 这是Django默认的
    'guardian.backends.ObjectPermissionBackend', # 这是guardian的
    )

    注意:一旦我们将django-guardian配置进我们的项目,当我们调用migrate命令将会创建一个匿名用户的实例(名为AnonymousUser )。guardian的匿名用户与Django的匿名用户不同。Django匿名用户在数据库中没有条目,但是Guardian匿名用户有。这意味着以下代码将返回意外的结果。

    request.user.is_anonymous = True

    额外设置

    GUARDIAN_RAISE_403

    如果GUARDIAN_RAISE_403设置为True,guardian将会抛出django.core.exceptions.PermissionDenied异常,而不是返回一个空的django.http.HttpResponseForbidden

    GUARDIAN_RENDER_403GUARDIAN_RAISE_403不能同时设置为True。否则将抛出django.core.exceptions.ImproperlyConfigured异常

    GUARDIAN_RENDER_403

    如果GUARDIAN_RENDER_403设置为True,将会尝试渲染403响应,而不是返回空的django.http.HttpResponseForbidden。模板文件将通过GUARDIAN_TEMPLATE_403来设置。

    ANONYMOUS_USER_NAME

    用来设置匿名用户的用户名,默认为AnonymousUser

    GUARDIAN_GET_INIT_ANONYMOUS_USER

    Guardian支持匿名用户的对象级权限,但是在我们的项目中,我们使用自定义用户模型,默认功能可能会失败。这可能导致guardian每次migrate之后尝试创建匿名用户的问题。将使用此设置指向的功能来获取要创建的对象。一旦获取,save方法将在该实例上被调用。

    默认值为guardian.ctypes.get_default_content_type

    GUARDIAN_GET_CONTENT_TYPE

    Guardian允许应用程序提供自定义函数以从对象和模型中检索内容类型。当类或类层次结构以ContentType非标准方式使用框架时,这是有用的。大多数应用程序不必更改此设置。

    例如,当使用django-polymorphic适用于所有子模型的基本模型上的权限时,这是有用的。在这种情况下,自定义函数将返回ContentType多态模型的基类和ContentType非多态类的常规模型。默认为guardian.ctypes.get_default_content_type

    示例项目

    准备模型和自定义权限

    假设我们有以下模型

    from django.db import models
    from django.contrib.auth.models import User
    # Create your models here.


    class Task(models.Model):
    summary = models.CharField(max_length=32)
    content = models.TextField()
    reported_by = models.ForeignKey(User)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
    permissions = (
    ('view_task', 'View task'),
    )

    说明:permissions使我们自定义的权限,当我们调用migrate命令的时候,view_task将会被添加到默认的权限集合中。默认情况下Django为每个模型注册3个权限 * add_模型名 * change_模型名 * delete_模型名

    分配对象权限

    我们可以使用guardian.shortcuts.assign_perm()方法可以为用户/组分配对象权限

    为用户分配权限

    >>> from django.contrib.auth.models import User
    >>> from todo.models import Task
    >>> from guardian.shortcuts import assign_perm
    >>> boss = User.objects.create(username="Big Boss") # 创建用户boss
    >>> joe = User.objects.create(username="joe") # 创建用户joe
    >>> task = Task.objects.create(summary="Some job", content="", reported_by=boss) # 创建Task对象
    >>> joe.has_perm('view_task', task) # 默认用户对这个对象没有权限
    False
    >>> assign_perm('view_task', joe, task) # 为用户joe分配权限
    <UserObjectPermission: UserObjectPermission object>
    >>> joe.has_perm('view_task', task)
    True

    为用户组分配权限

    >>> from django.contrib.auth.models import Group
    >>> group = Group.objects.create(name="employees")
    >>> assign_perm("change_task", group, task)
    <GroupObjectPermission: GroupObjectPermission object>
    >>> jack = User.objects.create(username="jack")
    >>> jack.has_perm('change_task', task)
    False
    >>> jack.groups.add(group)
    >>> jack.has_perm('change_task', task)
    True

    检查对象权限

    标准方式

    之前的例子我们已经用到了,我们可以使用用户实例的has_perm来检查是否有某种权限。

    在视图中使用

    除了Django提供的has_perm外,django-guardian还提供了一些常用的方法帮助我们检查对象权限

    get_perms

    >>> from guardian.shortcuts import get_perms
    >>> 'change_task' in get_perms(joe, task)
    False
    >>> 'change_task' in get_perms(jack, task)
    True

    建议尽量使用标准has_perm方法。但是对于Group实例,它不是那么容易,get_perms解决这个问题很方便,因为它接受UserGroup实例。如果我们需要做更多的工作,我们可以使用ObjectPermissionChecker这个低级类,我们会在下一个章节讲。也可以使用get_user_perms获得直接分配权限给用户(而不是从它的超级用户权限或组成员资格继承的权限)。同样的,get_group_perms仅返回其是通过用户组的权限。

    get_objects_for_user

    有时候我们需要根据特定的用户,对象的类型和提供的全新啊来获取对象列表,例如

    >>> from guardian.shortcuts import get_objects_for_user
    >>> get_objects_for_user(jack, 'todo.change_task')
    <QuerySet [<Task: Task object>]>
    >>> get_objects_for_user(jack, 'todo.view_task')
    <QuerySet []>

    ObjectPermissionChecker

    guardian.core.ObjectPermissionChecker用于检查特定对象的用户/组的权限。因为他缓存结果,因此我们可以在多次检查权限的代码的一部分中使用

    >>> from guardian.core import ObjectPermissionChecker
    >>> cheker = ObjectPermissionChecker(joe)
    >>> checker = ObjectPermissionChecker(joe)
    >>> checker.has_perm('view_task', task)
    True
    >>> checker.has_perm('change_task', task)
    False

    使用装饰器

    标准permission_required装饰器不允许检查对象权限。django-guardian随附两个装饰器,这可能有助于简单的对象权限检查,但请记住,在装饰视图被调用之前,这些装饰器会触发数据库——这意味着如果在视图中进行类似的查找,那么最可能的一个(或更多,取决于查找)会发生额外的数据库查询。

    在模板中使用

    django-guardian附带特殊模板标签guardian.templatetags.guardian_tags.get_obj_perms(),可以存储给定用户/组和实例对的对象权限。为了使用它,我们需要在模板中放置以下内容:

    {% load  guardian_tags  %}

    guardian.templatetags.guardian_tags.get_obj_perms(parser, token)返回给定用户或者组和对象(Model实例)的权限列表。

    调用格式为

    {% 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 %}

    移除对象权限

    删除对象权限和分配一样简单,我们使用guardian.shortcuts.remove_perm()来移除权限

    >>> remove_perm("veiw_task",joe, task)
    (0, {'guardian.UserObjectPermission': 0})
    >>> joe.has_perm("view_task", task)
    True
    >>> remove_perm("view_task",joe, task)
    (1, {'guardian.UserObjectPermission': 1})
    >>> joe.has_perm("view_task", task)
    False

    孤儿对象许可

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

    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  

    还有一个是定期调用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=StudyGrou
  • 相关阅读:
    sublime 标题乱码,内容正常
    解决PHP7+ngnix+yaf框架404的问题
    调用RPC接口出现错误:Yar_Client_Transport_Exception (16) curl exec failed 'Timeout was reached'
    xhprof安装和使用
    单点登录
    如何让局域网内Apache互相访问
    lnmp
    virtualbox
    微信省市区 Mysql数据库
    lnmp
  • 原文地址:https://www.cnblogs.com/miaoweiye/p/12955321.html
Copyright © 2011-2022 走看看