Як побудувати традиційну форму входу в систему

Дата оновлення перекладу 2023-06-15

Як побудувати традиційну форму входу в систему

Tip

Якщо вам потрібна форма входу, і ви зберігаєте користувачів у якійсь DB, то вам варто розглянути використання FOSUserBundle, який допомагає вам будувати ваш об'єкт User і надає безліч маршрутів і контролерів для загальних завдань на кшталт виконання входу, реєстрації та забутого пароля.

У цьому записі ви побудуєте традиційну форму входу в систему. Звичайно, коли користувач виконує вхід, ви можете завантажувати ваших користувачів звідки завгодно - наприклад, із DB. Дивіться, , щоб дізнатися більше.

Для початку, увімкніть форму входу в систему під вашим брандмауером:

1
2
3
4
5
6
7
8
9
10
# app/config/security.yml
security:
    # ...

    firewalls:
        main:
            anonymous: ~
            form_login:
                login_path: login
                check_path: login

Tip

login_path і check_path також можуть бути іменами маршруту (але не можуть мати обов'язкові підстановні знаки - наприклад, /login/{foo} де foo не має значення за замовчуванням).

Тепер, коли система безпеки ініціює процес аутентифікації, вона буде перенаправляти користувача на форму виконання входу /login. Реалізація цієї форми входу в систему - ваша робота. Для початку, створіть новий SecurityController всередині пакета:

1
2
3
4
5
6
7
8
// src/AppBundle/Controller/SecurityController.php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class SecurityController extends Controller
{
}

Далі, сконфігуруйте маршрут, який ви раніше використовували у вашій конфігурації form_login (login):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/AppBundle/Controller/SecurityController.php

// ...
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class SecurityController extends Controller
{
    /**
     * @Route("/login", name="login")
     */
    public function loginAction(Request $request)
    {
    }
}

Чудово! Далі, додайте логіку до loginAction(), яка відображає форму входу:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// src/AppBundle/Controller/SecurityController.php
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

public function loginAction(Request $request, AuthenticationUtils $authUtils)
{
    // отримати помилку входу, якщо вона є
    $error = $authUtils->getLastAuthenticationError();

    // останнє імʼя користувача, введене користувачем
    $lastUsername = $authUtils->getLastUsername();

    return $this->render('security/login.html.twig', array(
        'last_username' => $lastUsername,
        'error'         => $error,
    ));
}

Не дайте цьому контролеру збентежити вас. Як ви побачите через секунду, коли користувач відправляє форму, система безпеки автоматично обробляє надсилання форми для вас. Якщо користувач надсилає недійсне ім'я користувача або пароль, контролер зчитує помилку відправлення форми із системи безпеки, щоб пізніше відобразити її користувачеві.

Іншими словами, ваша робота - відобразити форму входу і будь-які помилки входу, які могли виникнути, але система безпеки сама піклується про перевірку надісланих імені користувача та пароля, а також про аутентифікацію користувача.

Наррешті, створіть шаблон:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{# app/Resources/views/security/login.html.twig #}
{# ... вы скорее всего расширите ваш базовый шаблон, вроде base.html.twig #}

{% if error %}
    <div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}

<form action="{{ path('login') }}" method="post">
    <label for="username">Username:</label>
    <input type="text" id="username" name="_username" value="{{ last_username }}" />

    <label for="password">Password:</label>
    <input type="password" id="password" name="_password" />

    {#
        Якщо ви хочете контролювати URL, на який буде перенаправлено користувача
        при успішному вході (більше деталей нижче)
        <input type="hidden" name="_target_path" value="/account" />
    #}

    <button type="submit">login</button>
</form>

Tip

Змінна error передається в шаблон, як екземпляр AuthenticationException. Він може містити більше інформації - навіть секретної - про помилку аутентифікації, тож використовуйте його з розумом!

Форма може виглядати як завгодно, але зазвичай вона дотримується деяких домовленостей:

  • Елемент <form> надсилає запит POST до маршруту login, оскільки це те, що ви сконфігурували під ключем form_login в security.yml;
  • Поле імені користувача має ім'я _username, а поле пароля - _password.

Tip

Насправді, все це можна сконфігурувати під ключем form_login. Дивіться , щоб дізнатися більше.

Caution

Ця форма входу в систему на даний момент не захищена від CSRF-атак. Прочитайте , щоб дізнатися, як захистити вашу форму.

Ось і все! Коли ви відправляєте форму, система безпеки автоматично перевірить акредитацію користувача і або аутентифікує його, або відправить користувача назад до форми входу, де буде відображено помилку.

Повторимо весь процес:

  1. Користувач намагається отримати доступ до захищеного ресурсу;
  2. Брандмауер ініціює процес аутентифікації, перенаправляючи користувача до форми входу в систему (/login);
  3. Сторінка /login відображає форму входу через маршрут і контролер, створені в цьому прикладі;
  4. Користувач відправляє форму входу в /login;
  5. Система безпеки приймає запит, перевіряє надіслану користувачем акредитацію, аутентифікує користувача, якщо все вірно, і відправляє користувача назад до форми входу - якщо ні.

Перенаправлення після успішного входу

Якщо відправлена акредитація вірна, користувача буде перенаправлено на сторінку, яка була запитана спочатку (наприклад, /admin/foo). Якщо користувач спочатку зайшов на сторінку входу в систему, його буде перенаправлений на домашню сторінку. Це можна налаштувати, і ви можете, наприклад, перенаправити користувача за конкретним URL.

Щоб дізнатися більше деталей про це і про те, як налаштувати процес форми входу у систему загалом, дивіться Як налаштувати відповіді аутентифікатора форми входу.

Уникайте розповсюджених пасток

Під час встановлення вашої форми входу в систему, будьте обережні з найпоширенішими пастками.

1. Створюйте правильні маршрути

По-перше, переконайтеся, що ви правильно визначили маршрут /login, і що він відповідає значенням конфігурації login_path і check_path. Неправильна конфігурація тут може означати, що ви будете перенаправлені на сторінку 404 замість сторінки входу, або що надсилання форми входу нічого не робитиме (ви просто будете бачити форму входу знову і знову).

2. Переконайтеся, що сторінка входу не захищена (цикл перенаправлення!)

Також, переконайтеся в тому, що сторінка входу доступна анонімним користувачам. Наприклад, наступна конфігурація, що вимагає роль ROLE_ADMIN для всіх URL (включно з URL /login), викликатиме цикл перенаправлення:

1
2
3
4
5
# app/config/security.yml

# ...
access_control:
    - { path: ^/, roles: ROLE_ADMIN }

Додавання керування доступом, що співпадає з /login/* і не вимагає аутентифікації, виправить цю проблему:

1
2
3
4
5
6
# app/config/security.yml

# ...
access_control:
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/, roles: ROLE_ADMIN }

3. Переконайтеся, що check_path знаходиться за брандмауером

Далі, переконайтеся, що ваш URL check_path (наприклад, /login) знаходиться за брандмауером, який ви використовуєте для вашої форми входу (у цьому прикладі, один брандмауер збігається з всіма URL, включно з /login). Якщо /login не збігається з жодним брандмауером, ви отримаєте виняток "Неможливо знайти контролер для шляху "/login"``.

4. Кілька брандмауерів не мають спільного контексту безпеки

Якщо ви використовуєте кілька брандмауерів, а аутентифікуєте за одним із них, то ви не будете аутентифікувати за всіма іншими автоматично. Різні брандмауери - це як різні системи безпеки. Щоб зробити це, вам потрібно ясно вказати однаковий для різних брандмауерів. Але зазвичай для більшості додатків одного брандмауера достатньо.

5. Маршрутизація сторінок помилок не покривається брандмауерами

Оскільки маршрутизація проходить до безпеки, сторінки помилок 404 не покриваються жодним брандмауером. Це означає, що ви не зможете перевірити безпеку або навіть отримати доступ до об'єкта користувача на цих сторінках. Дивіться Як налаштувати сторінки помилок, щоб дізнатися більше.