Symfony 5.3 — почему моя функция «Запомни меня» не работает?

#php #symfony #security #yaml #symfony5

Вопрос:

Я просто обновляю свое приложение symfony 4.4 до 5.3, чтобы использовать некоторые новые интересные вещи (UX, UUID,..). Поэтому я запустил новый проект и запустил make:auth команду, чтобы создать компоненты безопасности по последним значениям по умолчанию. Все работает идеально, за исключением функции «Запомни меня». Файл cookie просто не установлен (независимо от того, в каком браузере). Может быть, вы сможете мне помочь

безопасность.yaml

 security:
    enable_authenticator_manager: true
    password_hashers:
        AppEntityUser:
            algorithm: auto

    providers:
        app_user_provider:
            entity:
                class: AppEntityUser
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            custom_authenticator: AppSecurityLoginFormAuthenticator
            pattern: ^/
            logout:
                path: _logout
                target: _index
            remember_me:
                secret: '%env(APP_SECRET)%'
                lifetime: 31536000 # 1 year in seconds
                always_remember_me: true
                path: _index
            switch_user: true

    role_hierarchy:
        ROLE_USER: ROLE_USER
        ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH]

    access_control:
        - { path: ^/login, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/reset-password, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/datenschutz, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/impressum, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/worker, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/, role: ROLE_ADMIN }
        - { path: ^/, role: ROLE_USER }
 

Логинформ-аутентификатор

 <?php

namespace AppSecurity;

use SymfonyComponentHttpFoundationRedirectResponse;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingGeneratorUrlGeneratorInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentSecurityCoreSecurity;
use SymfonyComponentSecurityHttpAuthenticatorAbstractLoginFormAuthenticator;
use SymfonyComponentSecurityHttpAuthenticatorPassportBadgeCsrfTokenBadge;
use SymfonyComponentSecurityHttpAuthenticatorPassportBadgeUserBadge;
use SymfonyComponentSecurityHttpAuthenticatorPassportCredentialsPasswordCredentials;
use SymfonyComponentSecurityHttpAuthenticatorPassportPassport;
use SymfonyComponentSecurityHttpAuthenticatorPassportPassportInterface;
use SymfonyComponentSecurityHttpUtilTargetPathTrait;

class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
    use TargetPathTrait;

    public const LOGIN_ROUTE = '_login';

    private UrlGeneratorInterface $urlGenerator;

    public function __construct(UrlGeneratorInterface $urlGenerator)
    {
        $this->urlGenerator = $urlGenerator;
    }

    public function authenticate(Request $request): PassportInterface
    {
        $email = $request->request->get('email', '');

        $request->getSession()->set(Security::LAST_USERNAME, $email);

        return new Passport(
            new UserBadge($email),
            new PasswordCredentials($request->request->get('password', '')),
            [
                new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
            ]
        );
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
            return new RedirectResponse($targetPath);
        }

        return new RedirectResponse($this->urlGenerator->generate('_index'));
    }

    protected function getLoginUrl(Request $request): string
    {
        return $this->urlGenerator->generate(self::LOGIN_ROUTE);
    }

    public function supportsRememberMe(): bool
    {
        return true;
    }
}
 

логин.html.веточка

 {% extends 'base.html.twig' %}

{% block title %}{{ ('meta.title.login')|trans }}{% endblock %}

{% block body %}
<h1>{{ ('security.login.header')|trans }}</h1>

<form method="post">
    {% if error %}
        <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}

    <div class="mb-3">
        <label for="inputEmail" class="form-label">{{ ('security.login.email')|trans }}</label>
        <input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" autocomplete="email" required autofocus>
    </div>

    <div class="mb-3">
        <label for="inputPassword" class="form-label">{{ ('security.login.password')|trans }}</label>
        <input type="password" name="password" id="inputPassword" class="form-control" autocomplete="current-password" required>
    </div>

    <div class="mb-3">
        <a href="{{ path('app_forgot_password_request') }}">
            {{ ('button.forgot_password')|trans }}
        </a>
    </div>

    <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">

    <button class="btn btn-lg btn-outline-success" type="submit">
        {{ ('security.login.button')|trans }}
    </button>
</form>
{% endblock %}
 

Заранее большое спасибо!!

————— РЕДАКТИРОВАТЬ ———————-

Первоначально я задавал этот вопрос для версии 5.4, но он также актуален и для версии 5.3 — я попробовал и то, и другое, не установив файл cookie

Комментарии:

1. 5.4 еще не выпущен.

2. То же самое поведение для версии 5.3, которая выпущена

3. path Не должен быть фактический путь, а не название пути?

4. Честно говоря, это не имеет никакого эффекта. Я могу оставить его пустым для значения по умолчанию или ввести / или что-нибудь еще

5. Конфигурация выглядит нормально. Используйте сетевые инструменты вашего браузера, чтобы убедиться, что файл cookie REMEMBERME отправляется должным образом.

Ответ №1:

Я настроил тестовое приложение и подтвердил, что файл cookie «Запомни меня» не отправлялся, но затем я обманул и увидел подсказку на канале Symfony Slack. При использовании новой системы аутентификации на основе паспорта вам нужно использовать значок «Запомни меня». Это задокументировано здесь.

Поэтому обновите свой аутентификатор::аутентифицируйте метод с помощью:

         return new Passport(
            new UserBadge($email),
            new PasswordCredentials($request->request->get('password', '')),
            [
                new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
                new RememberMeBadge(),
            ]
        );
 

Все это, казалось, работало на меня.

Комментарии:

1. Это правда, я только что добавил этот значок, и теперь он работает и на меня

Ответ №2:

У меня была та же проблема, файл cookie REMEMBER был установлен, но не вызывался при удалении PHPSESSID . Потратив время, я нашел в своем классе пользовательских сущностей это (я добавил после обновления Symfony, не задумываясь об этом). :

 public function getUserIdentifier() {
  return $this->id;
}
 

Но в securiy.yml мой провайдер пользователей является :

     app_user_provider:
        entity:
            class: AppEntityUser
            property: email
 

Поэтому я изменил свою функцию getUserIdentifier на эту, и теперь она работает :

 public function getUserIdentifier() {
  return $this->email;
}
 

Комментарии:

1. Не работает в Symfony >=5.3.