Як додати функціональність входу у систему "Запамʼятати мене"

Дата оновлення перекладу 2022-12-22

Як додати функціональність входу у систему "Запамʼятати мене"

Коли користувач був аутентифікований, його посвідчення користувача зазвичай зберігаються у сесії. Це означає, що коли сесія завершується, користувач вийде з системи, і йому потрібно буде знову надати деталі входу у систему в наступний раз, коли йому знадобиться доступ до додатку. Ви можете дозволити користувачам залишатися у системі довше, ніж триває сесія, використвуючи кукі з опцією брандмауера remember_me:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# config/packages/security.yaml
security:
    # ...

    firewalls:
        main:
            # ...
            remember_me:
                secret:   '%kernel.secret%' # обовʼязково
                lifetime: 604800 # 1 тиждень в секундах
                # за замовчуванням, функція включається при встановленні
                # прапорцю у чекбоксі у формі входу (див. нижче), розкоментуйте
                # наступний рядок, щоб включати її завжди.
                #always_remember_me: true

Опця secret - єдина обовʼязкова опція, і вона використовується для підпису кукі запамʼятати мене. Розповсюджено використання параметру kernel.secret, який визначається з використанням змінної середовища APP_SECRET.

Після включення системи remember_me у конфігурації, є ще декілька речей, які треба зробити, щоб "запамʼятати мене" працювало коректно:

  1. Додати чекбокс для активації "запамʼятати мене" ;
  2. Використати аутнетифікатор, який підтримує "запамʼятати мене" ;
  3. Опціонально, сконфігурувати, як зберігаються та валідуються кукі "запамʼятати мене" .

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

Note

remember_me містить багато налаштувань для конфігурації кукі, створеного цією функцією. Див Налаштування кукі "запамʼятати мене", щоб побачити повний опис цих налаштувань.

Активація системи "запамʼятати мене"

Використання кукі "запамʼятати мене" не завжди доречно (наприклад, вам не варто використовувати його на спільному ПК). Тому за замовчуванням, Symfony вимагає від ваших користувачів обрати систему "запамʼятати мене" через параметр запиту.

Цей параметр запиту часто встановлюється через чекбокс у формі входу. Цей чекбокс повинен мати імʼя _remember_me:

1
2
3
4
5
6
7
8
9
10
11
{# templates/security/login.html.twig #}
<form method="post">
    {# ... ваші поля форми #}

    <label>
        <input type="checkbox" name="_remember_me" checked/>
        Keep me logged in
    </label>

    {# ... #}
</form>

Note

Опціонально, ви можете сконфігурувати користувацьке імʼя цього чекбоксу, використовуючи налаштування name у розділі remember_me.

Завжди активувати "запамʼятати мене"

Іноді вам може захотітися завжди активувати систему "запамʼятати мене", а не дозволяти користувачам обирати це самостійно. У таких випадках, ви можете використати налаштування always_remember_me:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# config/packages/security.yaml
security:
    # ...

    firewalls:
        main:
            # ...
            remember_me:
                secret: '%kernel.secret%'
                # ...
                always_remember_me: true

Тепер, жодний параметр запиту не відмічається і кожна успішна аутентифікація виробить кукі "запамʼятати мене".

Додати підтримку "запамʼятати мене" в аутентифікатор

Не всі методи аутентифікації підтримуютьь"запамʼятати мене" (наприклад, базова HTTP аутентифікація не має такої підтримки). Аутентифікатор демонструє підтримку, використовуючи RememberMeBadge у паспорті безпеки .

Після входу у систему, ви можете використати профілювальник безпеки, щоб побачити, чи наявний цей значок:

Без цього значка, "запамʼятати мене" не буде активовано (незважаючи на всі інші налаштування).

Додати підтримку "запамʼятати мене" у користувацькі аутентифікатори

Коли ви використовуєте користувацький аутентифікатор, ви повинні додавати RememberMeBadge вручну:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Service/LoginAuthenticator.php
namespace App\Service;

// ...
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;

class LoginAuthenticator extends AbstractAuthenticator
{
    public function authenticate(Request $request): Passport
    {
        // ...

        return new Passport(
            new UserBadge(...),
            new PasswordCredentials(...),
            [
                new RememberMeBadge(),
            ]
        );
    }
}

Налаштувати, як зберігаються токени "запамʼятати мене"

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

Symfony надає два способи валідації токенів "запамʼятати мене":

Токени, засновані на підписі
За замовчуванням, кукі "запамʼятати мене" міститьь підпис, заснований на властивостях користувача. Якщо властивості змінюються, підпис також змінюється, а вже згенеровані токени більше не вважаються валідними. Див. , щоб отримати більше інформації.
Стійкі токени
Стійкі токени зберігають всі згенеровані токени (наприклад, у базі даних). Це дозволяє вам інвалідувати токени, змінюючи рядки у базі даних. Див. , щоб отримати більше інформації.

Note

Ви також можете написати власний обробник "запамʼятати мене", створивши клас, який розширює AbstractRememberMeHandler (або реалізує RememberMeHandlerInterface). Потім ви можете сконфігурувати цей користувацький обробник, сконфігурувавши ID cервісу в опції service під remember_me.

Використання підписаних токенів "запамʼятати мене"

За замовчуванням, кукі "запамʼятати мене" містять хеш, який використовується для валідації кукі. Цей хеш обчислюється на засаді сконфігурованих властивостей підпису.

Ці властивості завжди додані у хеш:

  • Ідентифікатор користувача (повернений getUserIdentifier());
  • Часова відмітка закінчення строку дії.

Крім цього, ви можете сконфігурувати користувацькі властивості, використовуючи налаштування signature_properties (за замовчуванням password). Властивості вилучаються з обʼєкта користувача, що використовує компонент PropertyAccess (наприклад, використовує getUpdatedAt() або публічну властивість $updatedAt при використанні updatedAt).

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# config/packages/security.yaml
security:
    # ...

    firewalls:
        main:
            # ...
            remember_me:
                secret: '%kernel.secret%'
                # ...
                signature_properties: ['password', 'updatedAt']

У цьому прикладі, кукі "запамʼятати мене" більше не вважатиметься валідним, якщо updatedAt, пароль або ідентифікатор для цього користувача змінюється.

Tip

Властивості підпису дозволяють деяке просунуте використання без необхідності налаштування сховища для всіх токенів "запамʼятати мене". Наприклад, ви можете додати поле forceReloginAt до вашого користувача і до властивостей підпису. Таким чином, ви можете інвалідувати всі токени "запамʼятати мене" з користувача, змінивши цю часову відмітку.

Зберігання токенів "запамʼятати мене" у базі даних

Так як токени "запамʼятати мене" часто є довготривалими, ви можете захотіти зберігати їх у базі даних, щоб мати над ними повний контроль. Symfony постачається з підтримкою стійких токенів "запамʼятати мене".

Ця реалізація використовує постачальника токенів "запамʼятати мене" для зберігання та отримання токенів з бази даних. DoctrineBridge надає постачальника токенів, використовуючи Doctrine.

Ви можете включити цей постачальник токенів, використовуючи налаштування doctrine:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
# config/packages/security.yaml
security:
    # ...

    firewalls:
        main:
            # ...
            remember_me:
                secret: '%kernel.secret%'
                # ...
                token_provider:
                    doctrine: true

Це також вказує Doctrine створити таблицю для токенів "запамʼятати мене". Якщо ви використовуєте DoctrineMigrationsBundle, ви можете створити нову міграцію для цього:

1
2
3
4
$ php bin/console doctrine:migrations:diff

# та опціонально виконати міграцію локально
$ php bin/console doctrine:migrations:migrate

В інших випадках, ви можете використати команду doctrine:schema:update:

1
2
3
4
5
# отримати необхідний код SQL
$ php bin/console doctrine:schema:update --dump-sql

# запустити SQL у вашому клієнту БД, або дозволити команді виконати його за вас
$ php bin/console doctrine:schema:update --force

Реалізація користувацького постачальника токенів

Ви також можете створити користувацький постачальник токенів, створивши клас, який реалізує TokenProviderInterface.

Потім, сконфігуруйте ID сервісу вашого користувацького постачальника токенів як service:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
# config/packages/security.yaml
security:
    # ...

    firewalls:
        main:
            # ...
            remember_me:
                # ...
                token_provider:
                    service: App\Security\RememberMe\CustomTokenProvider

Форсування користувача до повторної аутентифікації до отримання доступу до певних джерел

Коли користувач повертається на ваш сайт, він аутентифікується автоматично на основі інформації, збережної у кукі "запамʼятати мене". Це дозволяє користувачу отримати доступ до захищених джерел, як ніби користувач дійсно пройшов аутентифікацію під час візиту сайту.

Однак, у деяких випадках, вам може захотітися змусити користувача насправді повторно аутентифікуватися до отримання доступу до певних джерел. Наприклад, ви можете не дозволяти користувачам "запамʼятати мене" змінювати пароль. Ви можете зробити це, скориставшись перевагами деяких спеціальних "атрибутів":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Controller/AccountController.php
// ...

public function accountInfo(): Response
{
    // дозволити будь-якому аутентифікованому користувачу - нам не важливо, чи він
    // щойно увійшов в систему, чи використав кукі "запамʼятати мене"
    $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');

    // ...
}

public function resetPassword(): Response
{
    // вимагати, щоб користувач увійшов у систему під час *цієї* сесії, якщо
    // він зайшов у систему через кукі "запамʼятати мене", його буде
    // перенаправлено на сторінку входу у систему
    $this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');

    // ...
}

Tip

Також є атрибут IS_REMEMBERED який надає доступ лише якщо користувач аутентифікований через механізм "запамʼятати мене".

Налаштування кукі "запамʼятати мене"

Конфігурація remember_me містить багато опцій для налаштування кукі, створеного системою:

name (значення за замовчуванням: REMEMBERME)
Імʼя кукі, використаного для того, щоб залишити вашого користувача у системі. Якщо ви включите функцію remember_me у декількох брандмауерах одного додатку, переконайтеся, що ви обрали різні імена для кукі кожного брандмауера. Інакше ви зіштовхнетесь з багатьма проблемами, повʼязаними з безпекою.
lifetime (значення за замовчуванням: 31536000 тобто, 1 рік в секундах)
Кількість секунд, після яких кукі завершить строк дії. Це визначає максимальний час між двома візитами користувача, щоб він залишався аутентифікованим.
path (значення за замовчуванням: /)
Шлях, де було використано кукі, асоційований з цією функцією. За замовчуванням, кукі буде застосовано до всього веб-сайту, але ви можете обмежити це до конкретного розділу (наприклад, /forum, /admin).
domain (значення за замовчуванням: null)
Домен, де було використано кукі, асоційованийз цією функцією. За замовчуванням, кукі використовують поточний домен, отриманий з $_SERVER.
secure (значення за замовчуванням: false)
Якщо true, кукі, асоційований з цією функцією, відправляється користувачу через захищене зʼєднання HTTPS.
httponly (значення за замовчуванням: true)
Якщо true, кукі, асоційований з цією функцією, доступний лише через HTTP-протокол. Це означає, що кукі не буде доступний скриптовим мовам типу JavaScript.
samesite (значення за замовчуванням: null)
Якщо встановлено як strict, кукі, асоційований з цією функцією, не буде відправлений разом з міжсайтовими запитами, навіть при переході по звичайному посиланню.