Форма аутентификации и авторизации Django в Python

Рассмотрим, как создать форму аутентификации и авторизацииDjango в Python, которая позволяет пользователям входить в систему, используя имя пользователя и пароль.

Содержание

Создание нового приложения

Сначала создайте новое приложение с именем users, выполнив команду startapp:

django-admin startapp users

Каталог проекта будет выглядеть так:

├── blog
├── db.sqlite3
├── manage.py
├── mysite
├── static
├── templates
└── users

Затем зарегистрируйте приложение пользователя в установленных приложениях settings.py проекта:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # local
    'blog.apps.BlogConfig',
    'users.apps.UsersConfig'
]

Создайте новый файл urls.py внутри приложения пользователя со следующим кодом:

from django.urls import path
from . import views

urlpatterns = []

Наконец, включите urls.py приложения пользователя в urls.py проекта с помощью функции include():

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('blog.urls')),
    path('',include('users.urls'))
]

Создание формы входа

  • Сначала создайте URL-адрес для входа в urls.py приложения пользователя:
from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.sign_in, name='login'),
]
  • Во-вторых, создайте файл forms.py в приложении пользователя и определите LoginForm, который наследуется от класса Form:
from django import forms


class LoginForm(forms.Form):
    username = forms.CharField(max_length=65)
    password = forms.CharField(max_length=65, widget=forms.PasswordInput)

LoginForm имеет два поля: имя пользователя и пароль.

  • В-третьих, создайте функцию sign_in() в файле views.py приложения пользователя для визуализации шаблона login.html:
from django.shortcuts import render
from .forms import LoginForm


def sign_in(request):
    if request.method == 'GET':
        form = LoginForm()
        return render(request, 'users/login.html', {'form': form})
  • В-четвертых, создайте каталог templates/users внутри приложения users:
mkdir templates
cd templates
mkdir users
  • В-пятых, создайте шаблон login.html внутри каталога templates/users, который расширяет шаблон base.html:
{% extends 'base.html' %}

{% block content %}
<form method="POST" novalidate>
	{% csrf_token %}
	<h2>Login</h2>
	{{form.as_p}}
	<input type="submit" value="Login" />
</form>

{% endblock content%}
  • В-шестых, откройте URL-адрес входа, и вы увидите форму входа:
http://127.0.0.1:8000/login

Форма входа

Если вы введете имя пользователя/пароль и нажмете кнопку «Войти», вы получите сообщение об ошибке, поскольку мы еще не добавили код, обрабатывающий HTTP-запрос POST.

  • В-седьмых, измените функцию sign_in() для обработки процесса входа в систему:
from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth import login, authenticate
from .forms import LoginForm


def sign_in(request):

    if request.method == 'GET':
        form = LoginForm()
        return render(request,'users/login.html', {'form': form})
    
    elif request.method == 'POST':
        form = LoginForm(request.POST)
        
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(request,username=username,password=password)
            if user:
                login(request, user)
                messages.success(request,f'Hi {username.title()}, welcome back!')
                return redirect('posts')
        
        # form is not valid or user is not authenticated
        messages.error(request,f'Invalid username or password')
        return render(request,'users/login.html',{'form': form})

Как это работает.

Сначала импортируйте функцию аутентификации и входа из модуля django.contrib.auth:

from django.contrib.auth import login, authenticate

Функция authenticate() проверяет имя пользователя и пароль. Если имя пользователя и пароль действительны, она возвращает экземпляр класса User или None в противном случае.

Функция login() регистрирует пользователя. Технически она создает идентификатор сеанса на сервере и отправляет его обратно в веб-браузер в виде файла cookie.

В последующем запросе веб-браузер отправляет идентификатор сеанса обратно на веб-сервер, Django сопоставляет значение cookie с идентификатором сеанса и создает объект User.

Во-вторых, проверьте имя пользователя и пароль с помощью функции authenticate(), если форма действительна:

user = authenticate(request, username=username, password=password)

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

if user:
   login(request, user)
   messages.success(request,f'Hi {user.username.title()}, welcome back!')
   return redirect('posts')

В противном случае создайте флэш-сообщение об ошибке и перенаправьте пользователя обратно на страницу входа:

messages.error(request,f'Invalid username or password')
return render(request,'users/login.html')

Если вы введете имя пользователя без пароля и нажмете кнопку «Войти», вы получите следующее сообщение об ошибке:

Пример ввода пользователя без пароля

Однако если вы введете правильное имя пользователя/пароль, вы будете перенаправлены на страницу списка публикаций с приветственным сообщением:

Приветственное сообщение

Добавление формы выхода

Сначала определите маршрут для выхода пользователя из системы:

from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.sign_in, name='login'),
    path('logout/', views.sign_out, name='logout'),
]

Во-вторых, определите функцию sign_out() в файле views.py, которая обрабатывает маршрут выхода из системы:

from django.shortcuts import render, redirect
from django.contrib import messages
from django.contrib.auth import login, authenticate, logout
from .forms import LoginForm

def sign_in(request):

    if request.method == 'GET':
        form = LoginForm()
        return render(request,'users/login.html', {'form': form})
    
    elif request.method == 'POST':
        form = LoginForm(request.POST)
        
        if form.is_valid():
            username = form.cleaned_data['username']
            password=form.cleaned_data['password']
            user = authenticate(request,username=username,password=password)
            if user:
                login(request, user)
                messages.success(request,f'Hi {username.title()}, welcome back!')
                return redirect('posts')
        
        # either form not valid or user is not authenticated
        messages.error(request,f'Invalid username or password')
        return render(request,'users/login.html',{'form': form})
    
    
        
def sign_out(request):
    logout(request)
    messages.success(request,f'You have been logged out.')
    return redirect('login')        

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

В-третьих, измените функцию sign_in() в файле views.py приложения пользователя:

def sign_in(request):

    if request.method == 'GET':
        if request.user.is_authenticated:
            return redirect('posts')
        
        form = LoginForm()
        return render(request,'users/login.html', {'form': form})
    
    elif request.method == 'POST':
        form = LoginForm(request.POST)
        
        if form.is_valid():
            username = form.cleaned_data['username']
            password=form.cleaned_data['password']
            user = authenticate(request,username=username,password=password)
            if user:
                login(request, user)
                messages.success(request,f'Hi {username.title()}, welcome back!')
                return redirect('posts')
        
        # either form not valid or user is not authenticated
        messages.error(request,f'Invalid username or password')
        return render(request,'users/login.html',{'form': form})

Функция request.user.is_authenticated возвращает True, если пользователь вошел в систему, или False в противном случае.

В-четвертых, измените шаблон base.html, включив в него ссылку для выхода из системы, если пользователь аутентифицирован, и ссылку для входа в противном случае:

{%load static %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="{% static 'css/style.css' %}" />
    <script src="{% static 'js/app.js' %}" defer></script>
    <title>My Site</title>
  </head>
  <body>
  	<header>
  		{%if request.user.is_authenticated %}
  			<span>Hi {{ request.user.username | title }}</span>
  			<a href="{% url 'logout' %}">Logout</a>
  		{%else%}
  			<a href="{% url 'login' %}">Login</a>
  		{%endif%}
  	</header>
  	<main>
	  	{% if messages %}
			<div class="messages">
			{% for message in messages %}
				<div class="alert {% if message.tags %}alert-{{ message.tags }}"{% endif %}>
					{{ message }}
				</div>
			{% endfor %}
			</div>
		{% endif %}
		    
	    {%block content%} 
	    {%endblock content%}
  	</main>
	
  </body>
</html>

Если вы зайдете на сайт, вы увидите ссылку для входа:

Ссылка для входа

При нажатии на ссылку входа откроется страница входа:

Страница входа

Если вы введете действительные имя пользователя и пароль и войдете в систему, вы увидите приветственное сообщение, а также ссылку для выхода из системы:

Приветственное сообщение и ссылка для выхода

Если нажать на ссылку выхода, произойдет перенаправление на страницу входа:

Перенаправление на страницу входа

Скрытие ссылок редактирования и удаления в списке постов

Если пользователь вошел в систему, request.user.is_authenticated имеет значение True. Таким образом, вы можете использовать этот объект для отображения и скрытия элементов страницы, независимо от того, вошел пользователь в систему или нет.

Например, следующий код скрывает ссылки редактирования и удаления в шаблоне blog/home.html, если пользователь аутентифицирован:

{% extends 'base.html' %}
	
{% block content %}
<h1>My Posts</h1>
	{% for post in posts %}
		<h2>{{ post.title }}</h2>
		<small>Published on {{ post.published_at | date:"M d, Y" }} by {{ post.author | title}}</small>
		<p>{{ post.content }}</p>
		
		{% if request.user.is_authenticated %}
		<p>
			<a href="{% url 'post-edit' post.id %}">Edit</a> 
			<a href="{% url 'post-delete' post.id%}">Delete</a>
		</p>
		{% endif %}
		
	{% endfor %}
{% endblock content %}

Защита защищенных страниц

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

Если функция представления имеет декоратор login_required и неаутентифицированный пользователь попытается запустить ее, Django перенаправит пользователя на страницу входа.

Мы защитим функции создания, обновления и удаления записей с помощью декоратора login_required.

Сначала установите URL-адрес входа в settings.py следующим образом:

LOGIN_URL = 'login'

Если этого не сделать, Django выполнит перенаправление на URL-адрес входа по умолчанию, который будет accounts/login/, а не users/login, как мы определили в этом проекте.

Во-вторых, измените views.py приложения блога, добавив декоратор @login_required к функциям create_post, edit_post и delete_post:

from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .models import Post
from .forms import PostForm


@login_required
def delete_post(request, id):
    post = get_object_or_404(Post, pk=id)
    context = {'post': post}

    if request.method == 'GET':
        return render(request, 'blog/post_confirm_delete.html', context)
    elif request.method == 'POST':
        post.delete()
        messages.success(request,  'The post has been deleted successfully.')
        return redirect('posts')


@login_required
def edit_post(request, id):
    post = get_object_or_404(Post, id=id)

    if request.method == 'GET':
        context = {'form': PostForm(instance=post), 'id': id}
        return render(request, 'blog/post_form.html', context)

    elif request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            form.save()
            messages.success(
                request, 'The post has been updated successfully.')
            return redirect('posts')
        else:
            messages.error(request, 'Please correct the following errors:')
            return render(request, 'blog/post_form.html', {'form': form})


@login_required
def create_post(request):
    if request.method == 'GET':
        context = {'form': PostForm()}
        return render(request, 'blog/post_form.html', context)
    elif request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(
                request, 'The post has been created successfully.')
            return redirect('posts')
        else:
            messages.error(request, 'Please correct the following errors:')
            return render(request, 'blog/post_form.html', {'form': form})


def home(request):
    posts = Post.objects.all()
    context = {'posts': posts}
    return render(request, 'blog/home.html', context)


def about(request):
    return render(request, 'blog/about.html')

Если вы откроете URL-адрес создания, обновления или удаления, например:

http://127.0.0.1/post/create

Он будет перенаправлен на страницу входа.

Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *