本文将从以下几方面讲述以下django的权限管理:
1、系统默认的权限系统本质
2、如何去验证权限
3、添加系统权限
下边是默认的权限系统中默认的三种用户状态:
available:指明用户是否被认为活跃的,一旦登录,就会被置于available;
staff:指明用户是否可以登录到后台管理站点:
superuser:超级用户状态,指明该用户缺省拥有所有权限。
简单介绍一下django中的django.contrib:django.contrib下是各个拥有独立功能的APP,这些app是django对那些需要重复实现的功能做的一些抽象,省得大部分开发人员进行重复开发。而django.contrib.auth就是其中的一个附件。所以要使用django的权限管理模块,你必须将django.contrib.auth 放在settings.py中的INSTALLED_APPS 集合中
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
)
django的权限认证功能是在 django.contrib.auth 中实现的,当把django.contrib.auth加入到INSTALLED_APPS中,由于权限管理中将权限跟每个单独的app中的某一个model做挂钩,而这些model类的关系需要contenttype应用进行管理,所以还需要安装django.contrib.contenttypes到INSTALLED_APPS中。再运行manage.py syncdb(1.7版本以后的可以直接运行manage.py migrate),就会在数据库中,建立如下几个表:
其实django的权限相关都记录在这几张表中,默认在auth_permission中,会为models中的所有表,分别创建add,delete和change三种权限,要为用户或是组赋予权限,可以直接操作auth_group_permissions或是auth_user_user_permissions表,当然更简单的方法是直接用django提供的admin后台,如下:
从上边图表可以看出,权限管理最主要的表是
auth_permission: 标记着具体的权限项
auth_user_user_permissions:标记着据图user对应的具体权限(不包括通过用户组获得的权限)
auth_group_permissions: 标记着具体用户组对应的具体权限
所以,用户的权限为:auth_user_user_permissions中记录的具体权限 跟 所在群对应的权限的并集
限定view函数只有在user具有相应权限的情况下才能被访问,这里有两种修饰符如:
from django.contrib.auth.decorators import permission_required
@permission_required('myApp.can_do_someting', login_url="/login/")
def my_view(request):
.........
#上面第一个参数是权限,格式:app_label.codename,第二个参数是可选的 login_url 参数,这个参数让你指定登录页面的 URL (缺省为 settings.LOGIN_URL )。
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(lambda u: u.has_perm('myApp.can_do_someting'))
def my_view(request):
..........
def my_view(request): if not request.user.has_perm('myApp.can_do_someting'): ....... else: .......
首先看一下修饰符permission_required的源码
def permission_required(perm, login_url=None, raise_exception=False):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if neccesary.
If the raise_exception parameter is given the PermissionDenied exception
is raised.
"""
def check_perms(user):
# First check if the user has the permission (even anon users)
if user.has_perm(perm):
return True
# In case the 403 handler should be called raise the exception
if raise_exception:
raise PermissionDenied
# As the last resort, show the login form
return False
return user_passes_test(check_perms, login_url=login_url)
从源码可以看出,一切的权限判断都是通过user的has_perm 函数去验证的,那user.has_perm的具体实现又是怎样呢?
class PermissionsMixin(models.Model):
"""
A mixin class that adds the fields and methods necessary to support
Django's Group and Permission model using the ModelBackend.
"""
is_superuser = models.BooleanField(_('superuser status'), default=False,
help_text=_('Designates that this user has all permissions without '
'explicitly assigning them.'))
groups = models.ManyToManyField(Group, verbose_name=_('groups'),
blank=True, help_text=_('The groups this user belongs to. A user will '
'get all permissions granted to each of '
'his/her group.'))
user_permissions = models.ManyToManyField(Permission,
verbose_name=_('user permissions'), blank=True,
help_text='Specific permissions for this user.')
class Meta:
abstract = True
def get_group_permissions(self, obj=None):
"""
Returns a list of permission strings that this user has through his/her
groups. This method queries all available auth backends. If an object
is passed in, only permissions matching this object are returned.
"""
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_group_permissions"):
if obj is not None:
permissions.update(backend.get_group_permissions(self,
obj))
else:
permissions.update(backend.get_group_permissions(self))
return permissions
def get_all_permissions(self, obj=None):
return _user_get_all_permissions(self, obj)
def has_perm(self, perm, obj=None):
"""
Returns True if the user has the specified permission. This method
queries all available auth backends, but returns immediately if any
backend returns True. Thus, a user who has permission from a single
auth backend is assumed to have permission in general. If an object is
provided, permissions for this specific object are checked.
"""
# Active superusers have all permissions.
if self.is_active and self.is_superuser:
return True
# Otherwise we need to check the backends.
return _user_has_perm(self, perm, obj)
def _user_has_perm(user, perm, obj):
for backend in auth.get_backends():
if hasattr(backend, "has_perm"):
if obj is not None:
if backend.has_perm(user, perm, obj):
return True
else:
if backend.has_perm(user, perm):
return True
return False
def get_backends():
backends = []
for backend_path in settings.AUTHENTICATION_BACKENDS:
backends.append(load_backend(backend_path))
if not backends:
raise ImproperlyConfigured('No authentication backends have been defined. Does AUTHENTICATION_BACKENDS contain anything?')
return backends
再来看一下django.contrib.auth.backends.ModelBackend类:
class ModelBackend(object):
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
if user.check_password(password):
return user
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a non-existing user (#20760).
UserModel().set_password(password)
def get_group_permissions(self, user_obj, obj=None):
"""
Returns a set of permission strings that this user has through his/her
groups.
"""
if user_obj.is_anonymous() or obj is not None:
return set()
if not hasattr(user_obj, '_group_perm_cache'):
if user_obj.is_superuser:
perms = Permission.objects.all()
else:
user_groups_field = get_user_model()._meta.get_field('groups')
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
perms = Permission.objects.filter(**{user_groups_query: user_obj})
perms = perms.values_list('content_type__app_label', 'codename').order_by()
user_obj._group_perm_cache = set(["%s.%s" % (ct, name) for ct, name in perms])
return user_obj._group_perm_cache
def get_all_permissions(self, user_obj, obj=None):
if user_obj.is_anonymous() or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = set(["%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()])
user_obj._perm_cache.update(self.get_group_permissions(user_obj))
return user_obj._perm_cache
def has_perm(self, user_obj, perm, obj=None):
if not user_obj.is_active:
return False
return perm in self.get_all_permissions(user_obj, obj)
def has_module_perms(self, user_obj, app_label):
"""
Returns True if user_obj has any permissions in the given app_label.
"""
if not user_obj.is_active:
return False
for perm in self.get_all_permissions(user_obj):
if perm[:perm.index('.')] == app_label:
return True
return False
def get_user(self, user_id):
UserModel = get_user_model()
try:
return UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
return None
到此,整个默认的权限管理中的权限验证我们就清晰了,接下来我们看一下怎么去添加、删除权限
从上边的权限admin后台管理页面可以看出,manage.py syncdb(或者manage.py migrate)命令为我们创建了每个models对应的add、change、delete权限,因为系统默认每一个有权限进入系统 的用户都可以看到系统的大部分功能,除了特俗的页面用权限来限制,如管理页面等。
然而,大多数场景下,系统默认创建的权限远远无法满足我们需求,那么我们怎么添加自己需要的权限,其实本质上都是一样,都是对auth_permission变添加数据项, 下边有三种办法进行权限的添加,强烈建议使用第一种,也是django提供给开发人员最直接的入口:
class Issue(models.Model):
'''
发布信息表
'''
class Meta:
permissions = (
('can_apply_issue', u'申请发布权限'),
('can_author_issue', u'审批发布权限'),
('can_author_repeal', u'审批撤销权限'),
)
name:必填。小于50个字符。例如:'Can do someting',会显示在上图之中。 content_type:必填。一个指向django_content_type数据库表,对于每一个Django模型,在这个表里面都有一个记录对应。 codename:必填。小于100个字符。例如:'can_do_someting'。
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
content_type = ContentType.objects.get(app_label='myApp', model='Mymodel')
permission = Permission.objects.create(codename='can_do_someting',
name='Can do someting',
content_type=content_type)
这样我们就可以添加我们自己需要的权限了。用法跟系统默认的权限用法一样。