Log in user using either email address or username in Django

后端 未结 13 2433
故里飘歌
故里飘歌 2020-12-02 08:34

I am trying to create an auth backend to allow my users to log in using either their email address or their username in Django 1.6 with a custom user model. The backend work

相关标签:
13条回答
  • 2020-12-02 09:09

    Those who are still struggling. Because of following multiple tutorials, we end up with this kind of a mess. Actually there are multiple ways to create login view in Django. I was kinda mixing these solutions in my Django predefined method of log in using

    def login_request(request):
        form = AuthenticationForm()
        return render(request = request,
                  template_name = "main/login.html",
                  context={"form":form})
    

    But now I have worked around this problem by using this simple approach without modifying the authentication backend.

    In root directory (mysite) urls.py

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('accounts/', include('django.contrib.auth.urls')),
    ]
    

    In your app directory urls.py

    urlpatterns = [
        path('accounts/login/', views.login_view, name='login'),
    ]
    

    In views.py

    def login_view(request):
        if request.method == 'POST':
            userinput = request.POST['username']
            try:
                username = User.objects.get(email=userinput).username
            except User.DoesNotExist:
                username = request.POST['username']
            password = request.POST['password']
            user = auth.authenticate(username=username, password=password)
    
            if user is not None:
                auth.login(request, user)
                messages.success(request,"Login successfull")
                return redirect('home')
            else:
                messages.error(request,'Invalid credentials, Please check username/email or password. ')
        return render(request, "registration/login.html")
    

    Finally, In templates (registration/login.html)

    <div class="container ">
      <h2>SIGN IN</h2>
        <form action="{% url 'login' %}" method="post">
          {% csrf_token %}
          <div class="form-group">
            <label for="Password">Username or Email:</label>
            <input type="text" class="form-control" placeholder="Enter User Name" name="username">
          </div>
           <div class="form-group">
            <label for="Password">Password:</label>
            <input type="password" class="form-control"  placeholder="Enter Password" name="password">
          </div>
          <button type="submit" class="btn btn-dark">Sign In</button> &ensp; <a  class="" href="{% url 'password_reset' %}">
            Forgot Password?
            </a>
          <p>Don't have an account? <a  class="" href="{% url 'blog:signup' %}">
            REGISTER NOW
            </a></p> 
        </form>
    </div>
    

    This is the easiest solution I came up with.

    0 讨论(0)
  • 2020-12-02 09:11

    I implemented the solution in my view. However, my users are not allowed to have emails as username during registration and also each email is unique.

    if request.method=="POST":  
        username = request.POST.get('username').lower()
        password = request.POST.get('password')
        '''check if the username is a valid email'''
        try:
            email = validate_email(username)
            username = User.objects.get(email=username).username
        except:
            pass
        user = authenticate(request, username=username, password=password)
        if user is not None:
            login(request,user)
        else:
            messages.error(request,("Error logging in."))
            return redirect('login')
    

    I am using validate_email so that my users can have @ in their usernames, @bestuser is a valid username but not a valid email. It works for me and I don't have to overwrite the authenticate method.

    0 讨论(0)
  • 2020-12-02 09:14

    Here's a work-around that doesn't require modifying the authentication backend at all.

    First, look at the example login view from 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.
            ...
    

    If authentication with the username fails we can check if there is an email match, get the corresponding username, and try to authenticate again.

    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.
            ...
    

    Similar to 1bit0fMe's example, email should be a unique field and there is the same (highly unlikely) downside that they mentioned.

    I would only recommend this approach if all login on your site is handled by a single view or form. Otherwise, it would be better to modify the authenticate() method itself in the backend to avoid creating multiple points of potential failure.

    0 讨论(0)
  • 2020-12-02 09:15

    Note that for the most solutions like these you should add email uniqueness validation for User model to avoid the authentication vulnerability

    # models.py
    from django.contrib.auth.models import AbstractUser
    
    
    class User(AbstractUser):
        objects = UserManager()
        email = models.EmailField(_('email address'), unique=True)
    
        class Meta:
            verbose_name = _('user')
            verbose_name_plural = _('users')
            db_table = 'auth_user'
            swappable = 'AUTH_USER_MODEL'
    

    and then you have to update settings.py defining the AUTH_USER_MODEL property

    AUTH_USER_MODEL = '[your_app_name].User'
    
    0 讨论(0)
  • 2020-12-02 09:17

    I thought I'd chuck my simpler approach in for anyone else who comes across this:

    # -*- 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)
    

    Note:

    • disregards USERNAME_FIELD, although you could add it back in pretty easily
    • case insensitive (you could just remove the __iexact's though to make it not)
    0 讨论(0)
  • 2020-12-02 09:17

    I know this is already answered, however I have found a real neat way to implement login with both e-mail and username using the Django auth views. I did not see anyone use this type of method so I thought I'd share it for simplicity's sake.

    from django.contrib.auth.models import User
    
    
    class EmailAuthBackend():
        def authenticate(self, username=None, password=None):
            try:
                user = User.objects.get(email=username)
                if user.check_password(raw_password=password):
                    return user
                return None
            except User.DoesNotExist:
                return None
    
        def get_user(self, user_id):
            try:
                return User.objects.get(pk=user_id)
            except User.DoesNotExist:
                return None
    

    Then in your settings.py add this

    AUTHENTICATION_BACKENDS = (
        'django.contrib.auth.backends.ModelBackend',
        'myapp.authentication.EmailAuthBackend',
    )
    
    0 讨论(0)
提交回复
热议问题