Symfony4多防护身份验证器重定向问题

时间:2019-02-13 10:55:22

标签: php symfony security authentication firewall

对于我的Symfony4应用,我想要:

  • 1个用于中局的防火墙
  • 1个用于后台的防火墙。

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

虽然在中层办公室的登录操作可以正常进行,但是当我尝试进入/admin部分时,我仍然会遇到重定向循环。

日志显示,admin防火墙在这里被正确地认为是很好的防火墙,但是尽管我将app_admin_login设置为可以进行匿名访问,但它一直重定向到入口点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

security.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');
    }
}

1 个答案:

答案 0 :(得分:0)

由于@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防火墙,管理员保护响应,并且在管理部分成功登录了重定向。