Symfony 5.3 - 为什么我的“记住我”功能不起作用?

Symfony 5.3 - why is my remember me functionality not working?

我只是将我的 symfony 4.4 应用程序升级到 5.3 以使用一些新的很酷的东西(UX、UUID、..)。所以我开始了一个新项目和 运行 make:auth 命令来创建最新默认的安全组件。一切都很完美,除了记住我的功能。只是没有设置 cookie(无论是哪个浏览器)。也许你能帮帮我

security.yaml

security:
    enable_authenticator_manager: true
    password_hashers:
        App\Entity\User:
            algorithm: auto

    providers:
        app_user_provider:
            entity:
                class: App\Entity\User
                property: email
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            lazy: true
            provider: app_user_provider
            custom_authenticator: App\Security\LoginFormAuthenticator
            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 }

LoginFormAuthenticator

<?php

namespace App\Security;

use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

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;
    }
}

login.html.twig

{% 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

我设置了一个测试应用程序并确认没有发送记住我的 cookie,但后来我作弊并在 Symfony Slack 频道上看到了提示。使用新的基于护照的身份验证系统时,您需要使用记住我徽章。这是 documented here.

因此更新您的 Authenticator::authenticate 方法:

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

这一切似乎都对我有用。

我遇到了同样的问题,cookie REMEMBERME 已设置但在删除 PHPSESSID 时未被调用。花了一些时间后,我在我的用户实体 class 中找到了这个(我在升级 Symfony 后没有考虑就添加了):

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

但在 secutiy.yml,我的用户提供者是:

    app_user_provider:
        entity:
            class: App\Entity\User
            property: email

所以我将我的 getUserIdentifier 函数更改为此,现在它可以工作了:

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