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个参数:
- perm,这个参数是一个字符串,代表一个许可,格式必须为<app>.<perm_codename>或者<perm_codename>。但是如果第三个参数是None,则必须为<app>.<perm_codename>格式。因此建议还是统一使用<app>.<perm_codename>格式。注意app并不是app的全路径,而是最后一级的模块名。这一点和INSTALL_APP中的app全路径不同,如果你的app module不只一级的话,这地方一定要注意。
- user_or_group,这个参数是一个User或者Group类型的对象。
- 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)
这样就搞定了,不过需要写一些必要的代码。
django(权限、认证)系统——第三方组件实现Object级别权限控制的更多相关文章
- django用户认证系统——拓展 User 模型
Django 用户认证系统提供了一个内置的 User 对象,用于记录用户的用户名,密码等个人信息.对于 Django 内置的 User 模型, 仅包含以下一些主要的属性: username,即用户名 ...
- Django之认证系统
Django之认证系统 cookie和session 1.cookie不属于http协议范围,由于http协议无法保持状态,但实际情况,我们却又需要“保持状态”,因此cookie就是在这样一个场景下诞 ...
- Django的认证系统—auth模块
Django的认证系统 auth模块的知识点总结: 1. 创建超级用户 python manage.py createsuperuser from django.contrib import auth ...
- django用户认证系统——拓展 User 模型2
Django 用户认证系统提供了一个内置的 User 对象,用于记录用户的用户名,密码等个人信息.对于 Django 内置的 User 模型, 仅包含以下一些主要的属性: username,即用户名 ...
- django用户认证系统——基本设置1
网站提供登录.注册等用户认证功能是一个常见的需求.因此,Django 提供了一套功能完整的.灵活的.易于拓展的用户认证系统:django.contrib.auth.在本教程中,我将向你展示 auth ...
- “Django用户认证系统”学习资料收集
首推追梦人物——Django用户认证系统 待续……
- django用户认证系统——重置密码7
当用户不小心忘记了密码时,网站需要提供让用户找回账户密码的功能.在示例项目中,我们将发送一封含有重置用户密码链接的邮件到用户注册时的邮箱,用户点击收到的链接就可以重置他的密码,下面是具体做法. 发送邮 ...
- django用户认证系统——修改密码6
再此之前我们已经完成了用户登录.注册.注销等功能,接下来让我们继续为用户提供修改密码的功能.该功能 Django 的 auth 应用也已经为我们提供,过程几乎和之前的登录功能完全一样. 编写修改密码模 ...
- Django用户认证系统(三)组与权限
Django的权限系统很简单,它可以赋予users或groups中的users以权限. Django admin后台就使用了该权限系统,不过也可以用到你自己的代码中. User对象具有两个ManyTo ...
随机推荐
- 关于最新的APP上架流程
苹果官方在2015年05-06月开发者中心进行了改版,网上的APP Store上架大部分都不一样了,自己研究总结一下,一个最新的上架教程以备后用 1.1.前期工作 首先你需要有一个苹果的开发者帐号,一 ...
- The 7th tip of DB Query Analyzer
The 7th tip of DB Query Analyzer MA Gen feng ( Guangdong Unitoll Services incorporated, Gu ...
- WebService学习--(一)webservice相关概念
一.序言 大家或多或少都听过 WebService(Web服务),有一段时间很多计算机期刊.书籍和网站都大肆的提及和宣传WebService技术,其中不乏很多吹嘘和做广告的成 分.但是不得不承认的是W ...
- oracle to_date 函数
update pamsodt0p10 set cursysdate = to_date('2014-12-29 00:00:00','yyyy-mm-dd hh24:mi:ss') where cu ...
- selenium获取百度账户cookies
[效果图] 效果图最后即为获取到的cookies,百度账户的cookies首次获取,需要手动登录,之后就可以注入cookies,实现免密登录. [代码] public class baiduCooki ...
- Day16 Django
学Django之前,先看下http基础,老师的网页地址: web框架 - Yuan先生 - 博客园 http://www.cnblogs.com/yuanchenqi/articles/7690561 ...
- ffmpeg转换参数和压缩输出大小的比率 参考 最新版本FFMPEG
https://blog.cnlabs.NET/3668.html ffmpeg 转换压缩比例 FFMPEG如果是压缩为FLV文件 3个编码可选1. -c:v flv 标准FLV编码 这个好处是速度快 ...
- Scala编程入门---函数式编程
高阶函数 Scala中,由于函数时一等公民,因此可以直接将某个函数传入其他函数,作为参数.这个功能是极其强大的,也是Java这种面向对象的编程语言所不具备的. 接收其他函数作为函数参数的函数,也被称作 ...
- java后台服务器实现极光推送
一.添加极光推送所需要的jar包,项目使用的maven,所以只需要在pom文件里添加jar包依赖 <dependency> <groupId>cn.jpush.api</ ...
- IDEA安装教程
1.下载安装程序A,链接:https://pan.baidu.com/s/1IAsGDbApfyNsHuS7_m0rdw 密码:fthp 2.下载一个配置程序B,下载安装之后,暂时不用管,之后会用到. ...