Symfony 4 - 自定义 GuardAuthenticator 不设置记住我的 cookie

Symfony 4 - Custom GuardAuthenticator doesn't set remember me cookie

我正在构建自定义 GuardAuthenticator 以在特定路由上使用令牌登录。根据 documentation if supportsRememberMe() returns true and remember_me is activated in the firewall, remember me cookie should be set, 但事实并非如此(尽管它如果我在另一条路线上使用表单登录身份验证,则会设置)。

路线:

/**
 * @Route("/login/token/{id}/{token}/{force}", defaults={"force"=0}, name="login_token")
 */
public function loginToken()
{

}

GuardAuthenticator:

<?php


namespace App\Security;


use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class TokenLoginAuthenticator extends AbstractGuardAuthenticator
{
    use TargetPathTrait;

    private $em;
    private $force;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function supports(Request $request)
    {
        return 'login_token' === $request->attributes->get('_route') && $request->isMethod('GET');
    }


    public function getCredentials(Request $request)
    {
        $credentials = [
            'id' => $request->attributes->get('id'),
            'token' => $request->attributes->get('token')
        ];

        $this->force = $request->attributes->get('force');

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $user = $this->em->getRepository(User::class)->find($credentials['id']);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('No user found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        if ($user->getToken() === $credentials['token']) {
            return true;
        }
        throw new HttpException(403, "Forbidden");
    }


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

    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        throw new HttpException(403, "Forbidden");
    }

    public function start(Request $request, AuthenticationException $authException = null)
    {
    }

    public function supportsRememberMe()
    {
        return true;
    }
}

安全配置:

security:
    encoders:
        App\Entity\User:
            id: 'App\Security\PasswordEncoder'

    providers:
        in_memory: { memory: ~ }
        orm:
            entity:
                class: App\Entity\User
                property: email

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            anonymous: true
            form_login:
                login_path: login
                check_path: login
                provider: orm
                csrf_token_generator: security.csrf.token_manager
                default_target_path: homepage

            logout:
                path:   /logout
                target: /

            remember_me:
                secret:   '%kernel.secret%'
                lifetime: 604800 # 1 week in seconds
                path: /
                # by default, the feature is enablered by checking a
                # checkbox in the login form (see below), uncomment the
                # following line to always enable it.
                # always_remember_me: true

            guard:
                provider: orm
                authenticators:
                    - App\Security\TokenLoginAuthenticator

如果满足以下 所有 条件,将设置记住我 cookie:

  • supportsRememberMe()方法returnstrue.
  • 防火墙中的remember_me键已配置。
  • (默认)_remember_me 参数在请求中发送。这通常是通过在登录表单中有一个 _remember_me 复选框来完成的(但它可以作为 url 参数(?_remember_me=1)发送,或者我们可以配置防火墙 remember_me 键至 always_remember_me.
  • onAuthenticationSuccess() 方法 returns 响应对象。

在 Symfony 5.2.6 中,不要忘记将新的 RememberMeBadge() 添加到身份验证函数中:

use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;

class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
    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')),
                new RememberMeBadge(),
            ]
        );
    }
}