使用django.contrib.auth,如何让用户知道他们的登录失败的原因?

时间:2012-07-14 00:46:27

标签: django django-authentication

我需要在失败的登录尝试中显示一条错误消息,指出用户是否处于非活动状态,密码是否错误或未找到用户。我正在对API进行身份验证,因此我制作了自定义身份验证后端。目前,为了证明客户端登录工作正常,我提出了一些名为UserNotFoundError,InactiveUserError和InvalidPasswordError的异常。

我很确定这不是正确的方法,如果我想使用Django的auth视图,我无论如何都无法捕捉到这些异常。

我假设我需要创建自己的身份验证视图。但是,有更好的方法吗?

(我知道提供登录失败的原因并不安全,但我必须这样做。)

2 个答案:

答案 0 :(得分:3)

这是完全未经测试的,并且无法保证可以正常工作,但您可以尝试这些方法。此解决方案唯一需要注意的是,您的后端必须是最后使用的身份验证后端(或者是唯一使用的身份验证后端)。

contrib.auth.views.authenticate方法接受authentication_form的参数,这意味着您应该能够使用扩展contrib.auth.forms.AuthenticationForm的自己的表单覆盖它。该形式调用后端的authenticate()方法。因此,在您的后端,您可以返回一些自定义对象,其中包含用户实例以及“失败”状态和“失败原因”,用于验证失败的原因。

如果您的自定义对象状态为失败,则可以抛出ValidationError,但该登录视图不会对用户进行任何进一步处理。

还要确保覆盖form.get_user(),以便它返回实际的User实例,而不是您的自定义对象。

答案 1 :(得分:0)

或者只是覆盖login(),如下:

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import logout
from django.http import HttpResponseRedirect
from django.template.response import TemplateResponse
from django.utils.translation import ugettext as _
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect

# Avoid shadowing the login() and logout() views below.
from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.sites.models import get_current_site

# App specific
from axes.models import AccessAttempt


@sensitive_post_parameters()
@csrf_protect
@never_cache
def login(request, template_name='registration/login.html',
          redirect_field_name=REDIRECT_FIELD_NAME,
          authentication_form=AuthenticationForm,
          current_app=None, extra_context=None,
          form_name='form'):
    """
    Displays the login form and handles the login action.
    """
    redirect_to = request.REQUEST.get(redirect_field_name, '')

    if request.method == "POST":
        form = authentication_form(data=request.POST)
        if form.is_valid():
            netloc = urlparse.urlparse(redirect_to)[1]

            # Use default setting if redirect_to is empty
            if not redirect_to:
                redirect_to = settings.LOGIN_REDIRECT_URL

            # Heavier security check -- don't allow redirection to a different
            # host.
            elif netloc and netloc != request.get_host():
                redirect_to = settings.LOGIN_REDIRECT_URL

            # Okay, security checks complete. Log the user in.
            auth_login(request, form.get_user())

            if request.session.test_cookie_worked():
                request.session.delete_test_cookie()

            messages.add_message(request, messages.SUCCESS, _("Welcome <strong>%s</strong>.") % [request.user.get_full_name() if request.user.get_full_name() is not u"" else request.user.username].pop())

            return HttpResponseRedirect(redirect_to)
        else:
            if extra_context is None:
                extra_context = {}
            extra_context.update({'login_form_errors': True})
            try:
                attempt = AccessAttempt.objects.get(ip_address=request.META.get('REMOTE_ADDR', ''))
                if attempt.failures_since_start < 2:
                    messages.add_message(request, messages.ERROR, _(u"Invalid credentials, last chance."))
                if attempt.failures_since_start == 2:
                    messages.add_message(request, messages.ERROR, _(u"Authentication has been blocked for a 24h cooldown period."))
            except AccessAttempt.DoesNotExist:
                messages.add_message(request, messages.ERROR, _(u"Invalid credentials, please try again."))
    else:
        form = authentication_form(request)

    request.session.set_test_cookie()

    current_site = get_current_site(request)

    context = {
        form_name: form,
        redirect_field_name: redirect_to,
        'site': current_site,
        'site_name': current_site.name,
        }
    if extra_context is not None:
        context.update(extra_context)
    return TemplateResponse(request, template_name, context,
        current_app=current_app)

注释:

  • 此示例使用django-axes
  • 出于明显的安全原因,为不良凭据提供用户特定错误是不好的做法