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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM