2014-08-14 2 views
9

Я пытаюсь создать автономный сервер, чтобы мои пользователи могли войти в систему, используя свой адрес электронной почты или свое имя пользователя в Django 1.6 с пользовательской моделью пользователя. Бэкэнд работает, когда я вхожу в систему с именем пользователя, но по какой-то причине не имеет адреса электронной почты. Есть ли что-то, чего я забываю?Войдите в систему, используя либо адрес электронной почты, либо имя пользователя в Django

from django.conf import settings 
from django.contrib.auth.models import User 

class EmailOrUsernameModelBackend(object): 
    """ 
    This is a ModelBacked that allows authentication with either a username or an email address. 

    """ 
    def authenticate(self, username=None, password=None): 
     if '@' in username: 
      kwargs = {'email': username} 
     else: 
      kwargs = {'username': username} 
     try: 
      user = User.objects.get(**kwargs) 
      if user.check_password(password): 
       return user 
     except User.DoesNotExist: 
      return None 

    def get_user(self, username): 
     try: 
      return User.objects.get(pk=username) 
     except User.DoesNotExist: 
      return None 

Edit: Как было предложено я унаследовал от ModelBackend и установил его в моих настройках В моих настройках у меня есть этот AUTHENTICATION_BACKENDS = ( 'users.backends', «django.contrib.auth.backends. ModelBackend», ) И я изменил бэкенд к этому:

from django.conf import settings 
from django.contrib.auth.models import User 
from django.contrib.auth.backends import ModelBackend 
class EmailOrUsernameModelBackend(ModelBackend): 
    """ 
    This is a ModelBacked that allows authentication with either a username or an email address. 

    """ 
    def authenticate(self, username=None, password=None): 
     if '@' in username: 
      kwargs = {'email': username} 
     else: 
      kwargs = {'username': username} 
     try: 
      user = User.objects.get(**kwargs) 
      if user.check_password(password): 
       return user 
     except User.DoesNotExist: 
      return None 

    def get_user(self, username): 
     try: 
      return User.objects.get(pk=username) 
     except User.DoesNotExist: 
      return None 

Теперь я получаю Module "users" does not define a "backends" attribute/class ошибку.

+1

Я думаю, что вы должны наследовать от 'django.contrib.auth.backends.ModelBackend' (и установить его в' AUTHENTICATION_BACKENDS' конечно). – thebjorn

+0

@thebjorn Я пробовал наследовать от ModelBackend, и у меня другая ошибка. Я отредактировал вопрос выше – user3282276

+0

Я предполагаю, что он исходит из первого элемента 'AUTHENTICATION_BACKENDS = ('users.backends', 'django.contrib.auth.backends.ModelBackend')'. Вероятно, это должно быть просто 'AUTHENTICATION_BACKENDS = ['yourapp.yourfile.EmailOrUsernameModelBackend']' – thebjorn

ответ

7

После выполнения рекомендаций, приведенных выше и измененных AUTHENTICATION_BACKENDS = ['yourapp.yourfile.EmailOrUsernameModelBackend'] Я получаю ошибку Manager isn't available; User has been swapped for 'users.User'. Это было вызвано тем, что я использовал модель пользователя по умолчанию вместо моей собственной. Вот рабочий код.

from django.conf import settings 
from django.contrib.auth import get_user_model 

class EmailOrUsernameModelBackend(object): 
    """ 
    This is a ModelBacked that allows authentication with either a username or an email address. 

    """ 
    def authenticate(self, username=None, password=None): 
     if '@' in username: 
      kwargs = {'email': username} 
     else: 
      kwargs = {'username': username} 
     try: 
      user = get_user_model().objects.get(**kwargs) 
      if user.check_password(password): 
       return user 
     except User.DoesNotExist: 
      return None 

    def get_user(self, username): 
     try: 
      return get_user_model().objects.get(pk=username) 
     except get_user_model().DoesNotExist: 
      return None 
3

Обновленная версия того же фрагмента, с улучшенной защитой. Кроме того, он позволяет включать или отключать проверку подлинности с учетом регистра. Если вы предпочитаете, вы можете install it directly from pypi.

from django.contrib.auth.backends import ModelBackend 
from django.contrib.auth import get_user_model 
from django.conf import settings 

################################### 
""" DEFAULT SETTINGS + ALIAS """ 
################################### 


try: 
    am = settings.AUTHENTICATION_METHOD 
except: 
    am = 'both' 
try: 
    cs = settings.AUTHENTICATION_CASE_SENSITIVE 
except: 
    cs = 'both' 

##################### 
""" EXCEPTIONS """ 
##################### 


VALID_AM = ['username', 'email', 'both'] 
VALID_CS = ['username', 'email', 'both', 'none'] 

if (am not in VALID_AM): 
    raise Exception("Invalid value for AUTHENTICATION_METHOD in project " 
        "settings. Use 'username','email', or 'both'.") 

if (cs not in VALID_CS): 
    raise Exception("Invalid value for AUTHENTICATION_CASE_SENSITIVE in project " 
        "settings. Use 'username','email', 'both' or 'none'.") 

############################ 
""" OVERRIDDEN METHODS """ 
############################ 


class DualAuthentication(ModelBackend): 
    """ 
    This is a ModelBacked that allows authentication 
    with either a username or an email address. 
    """ 

    def authenticate(self, username=None, password=None): 
     UserModel = get_user_model() 
     try: 
      if ((am == 'email') or (am == 'both')): 
       if ((cs == 'email') or cs == 'both'): 
        kwargs = {'email': username} 
       else: 
        kwargs = {'email__iexact': username} 

       user = UserModel.objects.get(**kwargs) 
      else: 
       raise 
     except: 
      if ((am == 'username') or (am == 'both')): 
       if ((cs == 'username') or cs == 'both'): 
        kwargs = {'username': username} 
       else: 
       kwargs = {'username__iexact': username} 

       user = UserModel.objects.get(**kwargs) 
     finally: 
      try: 
       if user.check_password(password): 
        return user 
      except: 
       # Run the default password hasher once to reduce the timing 
       # difference between an existing and a non-existing user. 
       UserModel().set_password(password) 
       return None 

    def get_user(self, username): 
     UserModel = get_user_model() 
     try: 
      return UserModel.objects.get(pk=username) 
     except UserModel.DoesNotExist: 
      return None 
+2

Действительно ли это код python ??? –

3

Я думал, что швырнуть мой простой подход для всех, кто попадается это:

# -*- coding: utf-8 -*- 
from django.contrib.auth import backends, get_user_model 
from django.db.models import Q 


class ModelBackend(backends.ModelBackend): 
    def authenticate(self, username=None, password=None, **kwargs): 
     UserModel = get_user_model() 

     try: 
      user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username)) 

      if user.check_password(password): 
       return user 
     except UserModel.DoesNotExist: 
      # Run the default password hasher once to reduce the timing 
      # difference between an existing and a non-existing user (#20760). 
      UserModel().set_password(password) 

Примечание:

  • пренебрегает USERNAME_FIELD, хотя вы можете добавить его в довольно легко
  • нечувствительный к регистру (вы могли бы просто удалить __iexact, хотя и сделать это не)
6

Еще одно решение:

from django.contrib.auth.backends import ModelBackend 
from django.contrib.auth import get_user_model 
from django.db.models import Q 


class DualModelBackend(ModelBackend): 

    def authenticate(self, username=None, password=None, **kwargs): 
     UserModel = get_user_model() 
     if username is None: 
      username = kwargs.get(UserModel.USERNAME_FIELD) 
     # `username` field does not restring using `@`, so technically email can be 
     # as username and email, even with different users 
     users = UserModel._default_manager.filter(
      Q(**{UserModel.USERNAME_FIELD: username}) | Q(email__iexact=username)) 
     # check for any password match 
     for user in users: 
      if user.check_password(password): 
       return user 
     if not users: 
      # Run the default password hasher once to reduce the timing 
      # difference between an existing and a non-existing user (#20760). 
      UserModel().set_password(password) 

Исправление:

  • По умолчанию @ не запрещено в поле имени пользователя, поэтому, если модель пользовательской Пользователь не запрещает @ символа, она не может быть используется для различения имени пользователя и электронной почты.
  • Технически могут быть два пользователя, использующих один и тот же адрес электронной почты, один в поле электронной почты, другой в имени пользователя. Если такая возможность не ограничена, это может привести к тому, что пользователь не сможет аутентифицироваться или необработанный исключение MultipleObjectsReturned, если используется UserModel._default_manager.get(Q(username__iexact=username) | Q(email__iexact=username)).
  • Ловля любое исключение с except:, как правило, плохая практика

Даунсайд - если есть два пользователя, используя один и тот же адрес электронной почты, один в имени пользователя, другой по электронной почте, и они имеют тот же самый пароль, то это склонный к аутентификации первого матча. Я предполагаю, что шансы на это маловероятны.

Также обратите внимание: любой из подходов должен обеспечивать уникальный email поле в модели пользователя, так как модель по умолчанию Пользователь не определяет уникальный адрес электронной почты, который приведет либо необработанное исключение в случае User.objects.get(email__iexact="...") используется, или аутентичности первый матч. В любом случае использование электронной почты для входа предполагает, что электронное письмо уникально.

0

Вот работа, которая не требует модификации аутентификации вообще.

Прежде всего, посмотрите на example login view от Django.

from django.contrib.auth import authenticate, login 

def my_view(request): 
    username = request.POST['username'] 
    password = request.POST['password'] 
    user = authenticate(username=username, password=password) 
    if user is not None: 
     login(request, user) 
     # Redirect to a success page. 
     ... 
    else: 
     # Return an 'invalid login' error message. 
     ... 

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

from django.contrib.auth import authenticate, login, get_user_model 

def my_view(request): 
    username = request.POST['username'] 
    password = request.POST['password'] 
    user = authenticate(username=username, password=password) 
    if user is None: 
     User = get_user_model() 
     user_queryset = User.objects.all().filter(email__iexact=username) 
     if user_queryset: 
      username = user_queryset[0].username 
      user = authenticate(username=username, password=password) 
    if user is not None: 
     login(request, user) 
     # Redirect to a success page. 
     ... 
    else: 
     # Return an 'invalid login' error message. 
     ... 

Аналогично примеру 1bit0fMe, в электронной почте должен быть уникальным поле и там же (очень маловероятно) недостаток, что они упоминали.

Я бы рекомендовал этот подход только в том случае, если весь вход на ваш сайт обрабатывается одним видом или формой. В противном случае было бы лучше изменить метод authenticate() в бэкэнд, чтобы избежать создания нескольких точек потенциального отказа.

0

Предполагая, что вы заблокировали/запретили использование имени пользователя с @, и вы хотите использовать модель пользователя django.

if request.method == 'POST': 
    form = LoginForm(request.POST) 
    if form.is_valid(): 
     cd=form.cleaned_data 
     if '@' in cd['username']: 
      username=User.objects.get(email=cd['username']).username 
     else: 
      username=cd['username'] 

     user = authenticate(username=username, 
           password=cd['password']) 

     if user is not None and user.is_active: 
      login(request,user) 
      return redirect('loggedin') 
     else: 
      return render(request, 'login.html') 
Смежные вопросы