zoukankan      html  css  js  c++  java
  • django的权限验证源码剖析

    class MyView(LoginRequiredMixin,PermissionRequiredMixin,View):
        permission_required = 'test.view_testmodel'
        def get(self,request):
            tests = Test.objects.all()
            return render(request,'test/test.html',locals())

    在上面这段代码中,PermissionRequiredMixin类是权限验证类,这个类在实例化的时候会调用他的dispath方法,permission_required 变量是要验证的权限,看看用户有没有该权限

    class PermissionRequiredMixin(AccessMixin):
        """Verify that the current user has all specified permissions."""
        permission_required = None
    
        def get_permission_required(self):
            """
            Override this method to override the permission_required attribute.
            Must return an iterable.
            """
            if self.permission_required is None:
                raise ImproperlyConfigured(
                    '{0} is missing the permission_required attribute. Define {0}.permission_required, or override '
                    '{0}.get_permission_required().'.format(self.__class__.__name__)
                )
            if isinstance(self.permission_required, str):
                perms = (self.permission_required,)
            else:
                perms = self.permission_required
            return perms
    
        def has_permission(self):
            """
            Override this method to customize the way permissions are checked.
            """
            perms = self.get_permission_required()
            return self.request.user.has_perms(perms)
    
        def dispatch(self, request, *args, **kwargs):
            if not self.has_permission():
                return self.handle_no_permission()
            return super().dispatch(request, *args, **kwargs)

    在has_permission方法中会获取要验证的权限(可以是一个,也可以是多个),self.request.user.has_perms(perms)这个方法在django.contrib.auth.models.AbstractUser的父类PermissionsMixin类中

    class PermissionsMixin(models.Model):
        """
        Add the fields and methods necessary to support the Group and Permission
        models 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 their groups.'
            ),
            related_name="user_set",
            related_query_name="user",
        )
        user_permissions = models.ManyToManyField(
            Permission,
            verbose_name=_('user permissions'),
            blank=True,
            help_text=_('Specific permissions for this user.'),
            related_name="user_set",
            related_query_name="user",
        )
    
        class Meta:
            abstract = True
    
        def get_group_permissions(self, obj=None):
            """
            Return a list of permission strings that this user has through their
            groups. Query all available auth backends. If an object is passed in,
            return only permissions matching this object.
            """
            permissions = set()
            for backend in auth.get_backends():
                if hasattr(backend, "get_group_permissions"):
                    permissions.update(backend.get_group_permissions(self, obj))
            return permissions
    
        def get_all_permissions(self, obj=None):
            return _user_get_all_permissions(self, obj)
    
        def has_perm(self, perm, obj=None):
            """
            Return True if the user has the specified permission. Query all
            available auth backends, but return 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, check
            permissions for that object.
            """
            # 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 has_perms(self, perm_list, obj=None):
            """
            Return True if the user has each of the specified permissions. If
            object is passed, check if the user has all required perms for it.
            """
            return all(self.has_perm(perm, obj) for perm in perm_list)
    
        def has_module_perms(self, app_label):
            """
            Return True if the user has any permissions in the given app label.
            Use similar logic as has_perm(), above.
            """
            # Active superusers have all permissions.
            if self.is_active and self.is_superuser:
                return True
    
            return _user_has_module_perms(self, app_label)

    在has_perm中会检查用户是不是超级管理员,如果不是就会执行_user_has_perm函数

    def _user_has_perm(user, perm, obj):
        """
        A backend can raise `PermissionDenied` to short-circuit permission checking.
        """
        for backend in auth.get_backends():
            if not hasattr(backend, 'has_perm'):
                continue
            try:
                if backend.has_perm(user, perm, obj):
                    return True
            except PermissionDenied:
                return False
        return False

    在global_setting中默认是AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend']

    def get_backends():
        return _get_backends(return_tuples=False)
    
    
    def _get_backends(return_tuples=False):
        backends = []
        for backend_path in settings.AUTHENTICATION_BACKENDS:
            backend = load_backend(backend_path)
            backends.append((backend, backend_path) if return_tuples else backend)
        if not backends:
            raise ImproperlyConfigured(
                'No authentication backends have been defined. Does '
                'AUTHENTICATION_BACKENDS contain anything?'
            )
        return backends
    
    def load_backend(path):
        return import_string(path)()
    
    
    def import_string(dotted_path):
        """
        Import a dotted module path and return the attribute/class designated by the
        last name in the path. Raise ImportError if the import failed.
        """
        try:
            module_path, class_name = dotted_path.rsplit('.', 1)
        except ValueError as err:
            raise ImportError("%s doesn't look like a module path" % dotted_path) from err
    
        module = import_module(module_path)
    
        try:
            return getattr(module, class_name)
        except AttributeError as err:
            raise ImportError('Module "%s" does not define a "%s" attribute/class' % (
                module_path, class_name)
            ) from err

    以上程序运行完成以后获取到django.contrib.auth.backends.ModelBackend的ModelBackend类对象,然后调用这个类的has_perm方法

    class ModelBackend:
        """
        Authenticates against settings.AUTH_USER_MODEL.
        """
    
        def authenticate(self, request, username=None, password=None, **kwargs):
            if username is None:
                username = kwargs.get(UserModel.USERNAME_FIELD)
            try:
                user = UserModel._default_manager.get_by_natural_key(username)
            except UserModel.DoesNotExist:
                # Run the default password hasher once to reduce the timing
                # difference between an existing and a nonexistent user (#20760).
                UserModel().set_password(password)
            else:
                if user.check_password(password) and self.user_can_authenticate(user):
                    return user
    
        def user_can_authenticate(self, user):
            """
            Reject users with is_active=False. Custom user models that don't have
            that attribute are allowed.
            """
            is_active = getattr(user, 'is_active', None)
            return is_active or is_active is None
    
        def _get_user_permissions(self, user_obj):
            return user_obj.user_permissions.all()
    
        def _get_group_permissions(self, user_obj):
            user_groups_field = get_user_model()._meta.get_field('groups')
            user_groups_query = 'group__%s' % user_groups_field.related_query_name()
            return Permission.objects.filter(**{user_groups_query: user_obj})
    
        def _get_permissions(self, user_obj, obj, from_name):
            """
            Return the permissions of `user_obj` from `from_name`. `from_name` can
            be either "group" or "user" to return permissions from
            `_get_group_permissions` or `_get_user_permissions` respectively.
            """
            if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
                return set()
    
            perm_cache_name = '_%s_perm_cache' % from_name
            if not hasattr(user_obj, perm_cache_name):
                if user_obj.is_superuser:
                    perms = Permission.objects.all()
                else:
                    perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj)
                perms = perms.values_list('content_type__app_label', 'codename').order_by()
                setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms})
            return getattr(user_obj, perm_cache_name)
    
        def get_user_permissions(self, user_obj, obj=None):
            """
            Return a set of permission strings the user `user_obj` has from their
            `user_permissions`.
            """
            return self._get_permissions(user_obj, obj, 'user')
    
        def get_group_permissions(self, user_obj, obj=None):
            """
            Return a set of permission strings the user `user_obj` has from the
            groups they belong.
            """
            return self._get_permissions(user_obj, obj, 'group')
    
        def get_all_permissions(self, user_obj, obj=None):
            if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
                return set()
            if not hasattr(user_obj, '_perm_cache'):
                user_obj._perm_cache = {
                    *self.get_user_permissions(user_obj),
                    *self.get_group_permissions(user_obj),
                }
            return user_obj._perm_cache
    
        def has_perm(self, user_obj, perm, obj=None):
            return user_obj.is_active and perm in self.get_all_permissions(user_obj, obj)
    
        def has_module_perms(self, user_obj, app_label):
            """
            Return True if user_obj has any permissions in the given app_label.
            """
            return user_obj.is_active and any(
                perm[:perm.index('.')] == app_label
                for perm in self.get_all_permissions(user_obj)
            )
    
        def get_user(self, user_id):
            try:
                user = UserModel._default_manager.get(pk=user_id)
            except UserModel.DoesNotExist:
                return None
            return user if self.user_can_authenticate(user) else None

    调用get_all_permissions方法获取用户的所有权限,这个方法中会获取用户和用户组所在用户组的全部权限,然后做成员判断,完成权限验证流程

  • 相关阅读:
    【LeetCode-回溯】组合总和
    MongoDB复制集成员类型
    Vant中的日期元素在iOS上显示NaN
    Vue风格
    Git设置代理和取消代理的方式
    吴晓波——疫情下的的“危”与“机”
    Vant库在PC端的使用
    买保险,不上当
    Vant的引入方式
    Duplicate keys detected: 'xxx'. This may cause an update error.
  • 原文地址:https://www.cnblogs.com/arrow-kejin/p/10796211.html
Copyright © 2011-2022 走看看