首页 > 解决方案 > Symfony4 多重保护验证器重定向问题

问题描述

对于我的 Symfony4 应用程序,我想:

为每个人配备一个专用的 Guard 身份验证器听起来不错(尤其是登录后重定向,获取登录页面......)。我曾经make创建过保护文件样板,最后得到了下面的文件。

虽然在中间办公室登录可以直接工作,但当我尝试去/adminpart时,我一直有一个重定向循环。

日志显示admin防火墙在这里被正确捕获为好的防火墙,但是尽管我app_admin_login为匿名访问设置了 OK,但它一直重定向到入口点,即admin/login. 为什么这样?

我肯定想念这里的东西,谢谢你的启发。

[2019-02-13 11:46:39] request.INFO: Matched route "easyadmin". {"route":"easyadmin","route_parameters":{"_controller":"Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction","path":"/admin/","permanent":true,"scheme":null,"httpPort":8900,"httpsPort":443,"_route":"easyadmin"},"request_uri":"http://localhost:8900/admin","method":"GET"} []
[2019-02-13 11:46:39] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2019-02-13 11:46:39] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/html/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
[2019-02-13 11:46:39] security.DEBUG: Calling Authentication entry point. [] []

[2019-02-13 11:46:39] request.INFO: Matched route "app_admin_login". {"route":"app_admin_login","route_parameters":{"_route":"app_admin_login","_controller":"App\\Controller\\SecurityController::adminLogin"},"request_uri":"http://localhost:8900/admin/login","method":"GET"} []
[2019-02-13 11:46:39] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2019-02-13 11:46:39] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/html/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
[2019-02-13 11:46:39] security.DEBUG: Calling Authentication entry point. [] []

[2019-02-13 11:46:39] request.INFO: Matched route "app_admin_login". {"route":"app_admin_login","route_parameters":{"_route":"app_admin_login","_controller":"App\\Controller\\SecurityController::adminLogin"},"request_uri":"http://localhost:8900/admin/login","method":"GET"} []
[2019-02-13 11:46:39] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"admin","authenticators":1} []
[2019-02-13 11:46:39] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"admin","authenticator":"App\\Security\\AdminLoginFormAuthenticator"} []
[2019-02-13 11:46:39] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationCredentialsNotFoundException(code: 0): A Token was not found in the TokenStorage. at /var/www/html/vendor/symfony/security-http/Firewall/AccessListener.php:51)"} []
[2019-02-13 11:46:39] security.DEBUG: Calling Authentication entry point. [] []
... Until max loop iteration number reached

安全.yaml

security:
    encoders:
        App\Entity\Shop\User:
            algorithm: argon2i

    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        # used to reload user from session & other features (e.g. switch_user)
        app_user_provider:
            entity:
                class: App\Entity\Shop\User
                property: email
    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        admin:
            pattern: ^/admin
            logout:
                path: app_logout
            guard:
                authenticators:
                    - App\Security\AdminLoginFormAuthenticator
#            logout_on_user_change: true
        main:
            pattern: ^/
            anonymous: true
            logout:
                path: app_logout
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
#            logout_on_user_change: true

            # activate different ways to authenticate

            # http_basic: true
            # https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate

            # form_login: true
            # https://symfony.com/doc/current/security/form_login_setup.html

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
         - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/admin, roles: ROLE_ADMIN }
         - { path: ^/, roles: ROLE_USER }

AdminLoginFormAuthenticator

class AdminLoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    private $request;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

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

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['email']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);

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

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $user = $token->getUser();

        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

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

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_admin_login');
    }
}

标签: phpsymfonysecurityauthenticationfirewall

解决方案


感谢@dbrumann 为我指明了正确的方向,我设法解决了问题。这只是一种解决方法,因为它不解释重定向本身,而是避免它

我重写了这个AbstratcFormLoginAuthenticator::start()方法:

/**
     * Overrided to avoid redirection loop
     * @param Request $request
     * @param AuthenticationException|null $authException
     *
     * @return RedirectResponse|Response
     * @throws \Exception
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        $url = $this->getLoginUrl();

        // If URL different than login one, redirect to login one
        if($url !== $this->urlGenerator->generate($request->attributes->get('_route'))) {
            return new RedirectResponse($url);
        }
        // Render login page as a subrequest for this call only
        else {
            $path['_controller'] = SecurityController::class . '::adminLogin';
            $subRequest = $request->duplicate([], null, $path);

            return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
        }
    }

重定向到admin/login页面是有效的,admin防火墙被调用,管理员保护响应和登录成功重定向在管理部分。


推荐阅读