Отдельная проверка имени пользователя и пароля во время аутентификации Django - PullRequest
5 голосов
/ 11 октября 2009

При использовании стандартного модуля аутентификации в django, неудачная аутентификация пользователя неоднозначна. А именно, кажется, нет никакого способа отличить следующие 2 сценария:

  • Имя пользователя действительное, пароль недействительный
  • Неверное имя пользователя

Я думаю, что я хотел бы отображать соответствующие сообщения пользователю в этих 2 случаях, а не одно "имя пользователя или пароль были недействительными ...".

Каждый имеет опыт работы с простыми способами сделать это. Суть дела, кажется, идет прямо к самому низкому уровню - в классе django.contrib.auth.backends.ModelBackend. Метод authenticate () этого класса, который принимает имя пользователя и пароль в качестве аргументов, просто возвращает объект User, если аутентификация прошла успешно, или None, если аутентификация не удалась. Учитывая, что этот код находится на самом низком уровне (ну, на самом низком уровне, который выше кода базы данных), в обход этого, похоже, выбрасывается много кода.

Является ли лучший способ просто внедрить новый бэкэнд аутентификации и добавить его в настройку AUTHENTICATION_BACKENDS? Можно реализовать серверную часть, которая возвращает кортеж (User, Bool), где объект User имеет значение только None, если имя пользователя не существует, а Bool имеет значение True, только если пароль был правильным. Это, однако, нарушило бы контракт, который бэкэнд имеет с методом django.contrib.auth.authenticate () (который задокументирован для возврата объекта User при успешной аутентификации и None в противном случае).

Может быть, это все из-за беспокойства? Независимо от того, было ли неправильное имя пользователя или пароль, пользователю, вероятно, придется в любом случае перейти на страницу «Потерянный пароль», так что, возможно, все это академично. Я просто не могу не чувствовать, хотя ...

EDIT:

Комментарий относительно ответа, который я выбрал: Ответ, который я выбрал, - это способ реализации этой функции. Ниже приведен еще один ответ, в котором обсуждаются возможные последствия этого для безопасности, который я также рассматривал как назначенный ответ. Тем не менее, указанный мной ответ объясняет , как может быть реализована. Ответ, основанный на безопасности, обсуждает, должен ли реализовать эту функцию, что на самом деле является другим вопросом.

Ответы [ 5 ]

19 голосов
/ 11 октября 2009

Вы действительно не хотите различать эти два случая. В противном случае вы даете потенциальному хакеру подсказку о том, является ли имя пользователя действительным или нет - значительная помощь для получения мошеннического входа в систему.

2 голосов
/ 12 октября 2009

Это не функция бэкенда, а просто форма аутентификации. Просто перепишите форму, чтобы отобразить ошибки, которые вы хотите для каждого поля. Напишите вид входа в систему, использующий вашу новую форму, и сделайте его URL-адресом по умолчанию. (На самом деле, я только что увидел в недавнем коммите Django, что теперь вы можете передать пользовательскую форму в представление входа в систему, так что это еще проще выполнить). Это должно занять около 5 минут усилий. Все, что вам нужно, находится в django.contrib.auth.

Для уточнения приведу текущую форму:

class AuthenticationForm(forms.Form):
    """
    Base class for authenticating users. Extend this to get a form that accepts
    username/password logins.
    """
    username = forms.CharField(label=_("Username"), max_length=30)
    password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)

    def __init__(self, request=None, *args, **kwargs):
        """
        If request is passed in, the form will validate that cookies are
        enabled. Note that the request (a HttpRequest object) must have set a
        cookie with the key TEST_COOKIE_NAME and value TEST_COOKIE_VALUE before
        running this validation.
        """
        self.request = request
        self.user_cache = None
        super(AuthenticationForm, self).__init__(*args, **kwargs)

    def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')

        if username and password:
            self.user_cache = authenticate(username=username, password=password)
            if self.user_cache is None:
                raise forms.ValidationError(_("Please enter a correct username and password. Note that both fields are case-sensitive."))
            elif not self.user_cache.is_active:
                raise forms.ValidationError(_("This account is inactive."))

        # TODO: determine whether this should move to its own method.
        if self.request:
            if not self.request.session.test_cookie_worked():
                raise forms.ValidationError(_("Your Web browser doesn't appear to have cookies enabled. Cookies are required for logging in."))

        return self.cleaned_data

    def get_user_id(self):
        if self.user_cache:
            return self.user_cache.id
        return None

    def get_user(self):
        return self.user_cache

Добавить:

def clean_username(self):
    username = self.cleaned_data['username']
    try:
        User.objects.get(username=username)
    except User.DoesNotExist:
        raise forms.ValidationError("The username you have entered does not exist.")
    return username
0 голосов
/ 19 сентября 2010
def clean_username(self):
    """
    Verifies that the username is available.
    """
    username = self.cleaned_data["username"]
    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        return username
    else:
        raise forms.ValidationError(u"""\
                This username is already registered, 
                please choose another one.\
                """)
0 голосов
/ 12 октября 2009

Этот ответ не относится только к Django, но это псевдокод, который я бы использовал для этого:

//Query if user exists who's username=<username> and password=<password>

//If true
    //successful login!

//If false
    //Query if user exists who's username=<username>
        //If true
            //This means the user typed in the wrong password
        //If false
            //This means the user typed in the wrong username
0 голосов
/ 11 октября 2009

Мы столкнулись с этим на сайте, который использовал службу подписки на внешнее членство. В основном вы делаете

from django.contrib.auth.models import User

try:
    user = User.objects.get(username=whatever)
    # if you get here the username exists and you can do a normal authentication
except:
    pass # no such username

В нашем случае, если имя пользователя не существует, мы должны были проверить файл HTPASSWD, который был обновлен сценарием Perl с внешнего сайта. Если имя существует в файле, то мы создадим пользователя, установим пароль и затем выполним аутентификацию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...