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方法获取用户的所有权限,这个方法中会获取用户和用户组所在用户组的全部权限,然后做成员判断,完成权限验证流程