简体   繁体   中英

Login_Required decorator not working properly in django

I am building a website in django. I used login_required decorator to prevent user to move to UserView without a successful login. But I came to problem where its not moving from login page even after a successful login authentication and stays on the login page. What am I doing wrong?

Here is my code:

view.py

def LoginView(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        try:
            user_info=User_Registration.objects.get(username=username)
           
            if user_info.password==password:
                return redirect("userview")
            else:

                   messages.info(request,"Invalid Password")
        except User_Registration.DoesNotExist:
            messages.info(request,"User Doesn't exist")
    else:

        return render(request,'login.html')

@login_required(login_url="login_view")
def UserView(request):
    return render(request,'user.html')

urls.py

from django.urls import path
from post_app import views

urlpatterns=[
    # path('',views.MainPageView.as_view()),
    path('/login',views.LoginView,name='login_view'),
    path('/userview',views.UserView,name='userview'),
    path('/registration',views.RegistrationView,name='registration_view'),
    path('new_job/<str:id>',views.post_new_job,name='new_job')
    
]

login.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link rel="stylesheet" href="{% static 'login.css' %}">
<!------ Include the above in your HEAD tag ---------->

<body>
    <div id="login">
        <h3 class="text-center text-white pt-5">Login form</h3>
        <div class="container">
            <div id="login-row" class="row justify-content-center align-items-center">
                <div id="login-column" class="col-md-6">
                    <div id="login-box" class="col-md-12">
                        <form id="login-form" class="form" action="" method="POST">
                            {% csrf_token %}
                            <h3 class="text-center text-info">Login</h3>
                            <div class="form-group">
                                <label for="username" class="text-info">Username:</label><br>
                                <input type="text" name="username" id="username" class="form-control">
                            </div>
                            <div class="form-group">
                                <label for="password" class="text-info">Password:</label><br>
                                <input type="password" name="password" id="password" class="form-control">
                            </div>
                            <div class="form-group">
                              
                                <input type="submit" name="submit" class="btn btn-info btn-md" value="submit">
                            </div>
                            <div id="register-link" class="text-right">
                                <a href="{% url 'registration_view' %}" class="text-info">Register here</a>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>


    <div>
        {% for message in messages %}
        <br>
        <h2  style="padding-left: 500px;" >{{message}}</h2>
        {% endfor %}
    </div>

</body>

</html>

Your code doesn't follow best practices for implementation of Authentication/Authorization in Django. Anyway, let me show how to fix your existing code to make it working.

def LoginView(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        try:
            user_info=User_Registration.objects.get(username=username)
           
            if user_info.password==password:
                return redirect("userview")
            else:
                messages.info(request,"Invalid Password")
        except User_Registration.DoesNotExist:
            messages.info(request,"User Doesn't exist")
    return render(request,'login.html')

@login_required(login_url="login_view")
def UserView(request):
    return render(request,'user.html')

So what was wrong? In the case when User Doesn't Exist or Invalid Password was entered - you do nothing, just add an error message. When the message is added you should render the template, where you will iterate over all messages. More about messages here https://docs.djangoproject.com/en/3.1/ref/contrib/messages/#enabling-messages

In the meantime, this still can be not working as expected. I have a set of questions to you:

  1. Do you get the password in raw format? I mean just a string with password, without hashing and any other protection.
  2. How do you store passwords in User_Registration model? Is it also just a string with password or the hashed string?

I also have a long list of further improvements in your code, but let's figure out the bug first.


UPDATE (22.09.2020)

Since you get page refresh and you're still on the login page - I think that redirect code wasn't called. That means you should see one of those messages you add. Please try to add messages to the context during template rendering. And let's check what messages you will see.

def LoginView(request):
    if request.method=='POST':
        username=request.POST.get('username')
        password=request.POST.get('password')
        try:
            user_info=User_Registration.objects.get(username=username)
           
            if user_info.password==password:
                return redirect("userview")
            else:
                messages.info(request,"Invalid Password")
        except User_Registration.DoesNotExist:
            messages.info(request,"User Doesn't exist")
    return render(request,'login.html', {
        'messages': messages.get_messages(request)
    })

@login_required(login_url="login_view")
def UserView(request):
    return render(request,'user.html')

If still you will not get any message, we can try replace both messages.info with raise . So the application will fail in one the places. But let's try passing messages explicitly first.

The checklist for getting messages to work https://docs.djangoproject.com/en/dev/ref/contrib/messages/#enabling-messages

  1. 'django.contrib.messages' is in INSTALLED_APPS
  2. MIDDLEWARE contains 'django.contrib.sessions.middleware.SessionMiddleware' and 'django.contrib.messages.middleware.MessageMiddleware'.
  3. The 'context_processors' option of the DjangoTemplates backend defined in your TEMPLATES setting contains 'django.contrib.messages.context_processors.messages'

views.py:

 @login_required
 def UserView(req):
     return render(req,"user.html")

setting.py:

LOGIN_URL = '/accounts/login/'  #Define your login page url..

In your views.py:

    if user_info.password == password: # I am not sure this is good way.
        return redirect("userview") # You cannot login here. If password equal password. This will redirect userview without login

Please use like this:

   from django.contrib.auth import login, authenticate # You need add this functions
   
   def LoginView(request):
       if request.method=='POST':
           username=request.POST.get('username')
           password=request.POST.get('password')
           user = authenticate(username=username, password=password) # You can control user with this function. If username and password are correct, this function will return this user. If incorrect, this will return None.
           if user is None:
                messages.info(request,"Username or password incorrect")
                return redirect("login_view")
           else:
               login(request, user) # You can login with this function
               messages.success(request, "You have successfully logged in")
               return redirect("userview")
        return render(request,'login.html')
    
    @login_required(login_url="login_view") # And now You can access this view. Because you logged in with login function
    def UserView(request):
        return render(request,'user.html')

My English skill is not good, but i tried to explain.

Redirct method accepts 3 types of arguments;

  1. A model: the model's get_absolute_url() function will be called.
  2. A view name, possibly with arguments: reverse() will be used to reverse-resolve the name.
  3. An absolute or relative URL, which will be used as-is for the redirect location.

Make sure that you are using correct view name.

After researching a for the similar problems, You can try:

  • Removing cache from the browser.

Also, as login url is static for the website. You can define your login_url in settings.py like:

LOGIN_URL = '/post_app/login'

Then, you don't have to define the login_url everywhere you use it. Else, everything looks good.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM