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
  • 相关阅读:
    rs
    stm32f767 usoc3
    stm32f767 RTT 日志
    stm32f767 标准库 工程模板
    stm32f767 HAL 工程模板
    docker tab 补全 linux tab 补全
    docker anconda 依赖 下载 不了
    docker run 常用 指令
    linux scp 命令
    Dockerfile 常用参数说明
  • 原文地址:https://www.cnblogs.com/miaoweiye/p/12955321.html
Copyright © 2011-2022 走看看