用源码告诉你django权限管理是怎么回事

本文将从以下几方面讲述以下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,就会在数据库中,建立如下几个表:

  • | auth_group |
  • | auth_group_permissions |
  • | auth_permission |
  • | auth_user |
  • | auth_user_groups |
  • | auth_user_user_permissions |
  • | django_content_type |

其实django的权限相关都记录在这几张表中,默认在auth_permission中,会为models中的所有表,分别创建add,delete和change三种权限,要为用户或是组赋予权限,可以直接操作auth_group_permissions或是auth_user_user_permissions表,当然更简单的方法是直接用django提供的admin后台,如下:

用源码告诉你django权限管理是怎么回事_第1张图片

从上边图表可以看出,权限管理最主要的表是 

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):
   ..........

二、是在view函数中去判断:

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)

由于默认的user继承了PermissionsMixin类,又没有重载has_perm函数,所以这里调用到的就是PermissionsMixin类的has_perm函数,继续跟踪下去可以看到

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

auth的get_backends()如下:

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

settings.AUTHENTICATION_BACKENDS的默认值是‘django.contrib.auth.backends.ModelBackend’,

再来看一下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

最后调用的是ModelBackend.has_perm --> ModelBackend.get_all_permissions,而从ModelBackend.get_all_permissions的实现我们就可以知道上边所说的: 用户的权限为:auth_user_user_permissions中记录的具体权限 跟 所在群对应的权限的并集


到此,整个默认的权限管理中的权限验证我们就清晰了,接下来我们看一下怎么去添加、删除权限

添加系统权限

从上边的权限admin后台管理页面可以看出,manage.py syncdb(或者manage.py migrate)命令为我们创建了每个models对应的add、change、delete权限,因为系统默认每一个有权限进入系统 的用户都可以看到系统的大部分功能,除了特俗的页面用权限来限制,如管理页面等。

然而,大多数场景下,系统默认创建的权限远远无法满足我们需求,那么我们怎么添加自己需要的权限,其实本质上都是一样,都是对auth_permission变添加数据项, 下边有三种办法进行权限的添加,强烈建议使用第一种,也是django提供给开发人员最直接的入口:

一、通过在每个model类中的元类(Meta)中添加permission 属性,再运行manage.py syncdb(或者manage.py migrate)命令:

class Issue(models.Model):
    '''
    发布信息表
    '''
    class Meta:
        permissions = (
            ('can_apply_issue', u'申请发布权限'),
            ('can_author_issue', u'审批发布权限'),
            ('can_author_repeal', u'审批撤销权限'),
        )


二、直接在修改auth_permission表,在其中加入相应的字段(少用为妙):

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)

这样我们就可以添加我们自己需要的权限了。用法跟系统默认的权限用法一样。


下一篇文章为大家讲解如何使得自定义的USER 能使用这套权限管理系统。


你可能感兴趣的:(django,权限管理,django,验证)