在登录过程中带有(user.is_active = False)标志的用户的消息

时间:2019-04-10 09:59:14

标签: django django-forms django-authentication

我正在尝试在登录过程中为拥有帐户但已被停用的用户添加消息,如果他想进入该帐户,则必须激活该消息。

我使用LoginView控制器,该控制器使用称为AuthenticationForm的内置标准格式

AuthenticationForm具有以下方法:


def confirm_login_allowed(self, user):
    """
    Controls whether the given User may log in. This is a policy setting,
    independent of end-user authentication. This default behavior is to
    allow login by active users, and reject login by inactive users.

    If the given user cannot log in, this method should raise a
    ``forms.ValidationError``.

    If the given user may log in, this method should return None.
    """
    if not user.is_active:
        raise forms.ValidationError(
            self.error_messages['inactive'],
            code='inactive',

# and list of error messages within this class

error_messages = {
    'invalid_login': _(
        "Please enter a correct %(username)s and password. Note that both "
        "fields may be case-sensitive."
    ),
    'inactive': _("This account is inactive."),
}

从技术上讲,如果不是user.is_active,它应该显示消息“ inactive”,但对于使用is_active = False DB表的非活动用户来说,它显示消息“ invalid_login”。 我正在尝试100%正确的登录名和密码,并且用户未处于活动状态,但显示了“ invalid_login”消息。然后,我只需将DB中的is_active标志打开为True,就可以轻松进入。 您知道为什么会这样吗?

最终目标是向拥有帐户但已被停用的用户显示此消息“'inactive':_(“此帐户处于非活动状态。”)“。 (或自定义消息) 从技术上讲,它应该工作,但不能。 预先感谢您,如果您发现此问题是基本问题或愚蠢的问题,我们会感到抱歉。

尝试:


class AuthCustomForm(AuthenticationForm):
    def clean(self):
        AuthenticationForm.clean(self)
        user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
        if not user.is_active and user:
            messages.warning(self.request, 'Please Activate your account',
                             extra_tags="", fail_silently=True)
           # return HttpResponseRedirect(' your url'))

最后有什么帮助:


class AuthCustomForm(AuthenticationForm):

    def get_invalid_login_error(self):

        user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))

        if not user.is_active and user:
            raise forms.ValidationError(
                self.error_messages['inactive'],
                code='inactive',)
        else:
            return forms.ValidationError(
                self.error_messages['invalid_login'],
                code='invalid_login',
                params={'username': self.username_field.verbose_name},
            )

这是一种奇怪的方法,因为DJANGO内置代码应该可以工作。我不确定我是否已解决我自己在这里之前犯的错误。也许我让事情变得更糟。

3 个答案:

答案 0 :(得分:7)

这是一个很长的答案,但希望它将对您有所帮助,并提供一些有关幕后工作方式的见解。

要了解为什么没有为不活动的用户举起'inactive' ValidationError,我们必须首先研究如何实现LoginView,特别是其{{1} }方法。

post

def post(self, request, *args, **kwargs): """ Handle POST requests: instantiate a form instance with the passed POST variables and then check if it's valid. """ form = self.get_form() if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) 收到包含表单数据的LoginView请求时,将调用此方法。 POST用请求中的get_form数据填充AuthenticationForm,然后检查该表单,根据其是否有效返回不同的响应。我们关心表单检查,因此让我们看一下POST方法的作用。

Django docs很好地解释了表单和字段验证的工作原理,因此我不会赘述。基本上,我们需要知道的是,当调用表单的is_valid方法时,表单首先会分别验证其所有字段,然后调用其is_valid方法来进行任何表单范围的验证。

在这里,我们需要了解clean的实现方式,因为它定义了自己的AuthenticationForm方法。

clean

这就是您确定的def clean(self): username = self.cleaned_data.get('username') password = self.cleaned_data.get('password') if username is not None and password: self.user_cache = authenticate(self.request, username=username, password=password) if self.user_cache is None: raise self.get_invalid_login_error() else: self.confirm_login_allowed(self.user_cache) return self.cleaned_data 方法起作用的地方。我们看到用户名和密码已传递给confirm_login_allowed函数。这将根据authenticate设置定义的所有身份验证后端检查给定的凭据(有关更多信息,请参见Django docs),如果成功,则返回经过身份验证的用户的AUTHENTICATION_BACKENDS模型,并返回User如果没有。

然后检查None的结果。如果它是authenticate,则无法对用户进行身份验证,并且None 'invalid_login'会按预期引发。如果不是,则表明用户已通过身份验证,如果用户处于非活动状态,则ValidationError会引发confirm_login_allowed 'inactive'


那为什么不提出ValidationError 'inactive'

这是因为非活动用户无法通过身份验证,因此ValidationError返回authenticate,这意味着将调用None而不是get_invalid_login_error


为什么非活动用户无法通过身份验证?

要查看此信息,我将假设您没有使用自定义身份验证后端,这意味着您的confirm_login_allowed设置被设置为默认值:AUTHENTICATION_BACKENDS。这意味着['django.contrib.auth.backends.ModelBackend']是唯一使用的身份验证后端,我们可以看看它的ModelBackend方法,它是以前见过的authenticate函数在内部调用的方法。

authenticate

我们对最后一个def authenticate(self, request, username=None, password=None, **kwargs): if username is None: username = kwargs.get(UserModel.USERNAME_FIELD) if username is None or password is None: return 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 语句感兴趣。

if

对于不活动的用户,我们知道密码正确,因此if user.check_password(password) and self.user_can_authenticate(user): return user 将返回check_password。这意味着它必须是True方法,该方法返回user_can_authenticate并导致不活动的用户无法通过身份验证。等等,因为我们快到了...

False

啊哈!如果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 user_can_authenticateFalse返回user.is_active,这会导致用户无法进行身份验证。


解决方案

我们可以继承False的子类,覆盖ModelBackend,并将user_can_authenticate的设置指向这个新的子类。

app / backends.py

AUTHENTICATION_BACKENDS

settings.py

from django.contrib.auth import backends


class CustomModelBackend(backends.ModelBackend):
    def user_can_authenticate(self, user):
        return True


我认为此解决方案比更改AUTHENTICATION_BACKENDS = [ 'app.backends.CustomModelBackend', ] 的逻辑更干净。

然后,您可以通过将get_invalid_login_error子类化,覆盖'inactive'并设置ValidationError的{​​{1}}属性来覆盖AuthenticationForm error_messages消息到这个新的子类。

authentication_form

答案 1 :(得分:0)

您可以尝试这个。

user = authenticate(username=username, password=password)
if user and user.is_active==False:
   messages.warning(request, 'Please Activate your account', extra_tags="")
   return HttpResponseRedirect(' your url'))

答案 2 :(得分:0)



class AuthCustomForm(AuthenticationForm):

    def get_invalid_login_error(self):

        user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))

        if not user.is_active and user:
            raise forms.ValidationError(
                self.error_messages['inactive'],
                code='inactive',)
        else:
            return forms.ValidationError(
                self.error_messages['invalid_login'],
                code='invalid_login',
                params={'username': self.username_field.verbose_name}, )