symfony 4:成功通过身份验证后,它将重定向到管理区域,并使用匿名令牌填充TokenStorage

问题描述

我有一个旧的Symfony 3.1网站,我先升级到Symfony 3.4.x,然后又升级到Symfony 4.4.11,但是我没有将其升级到symfony flex。我修复了许多问题,公共站点似乎正常运行。

我不得不重建身份验证,因为旧的与sf4不兼容。

我遵循了 https://symfony.com/doc/4.4/security/form_login_setup.html

和: https://symfonycasts.com/screencast/symfony-security/make-user

我遇到了这样一种情况,即在成功认证之后,当它重定向到管理区域时,它总是再次检查LoginFormAuthenticator,这显然不支持管理区域,并且它以匿名用户的身份重定向回到登录页面。 / p>

关于此问题,有许多讨论,并尝试了所有我发现的问题,但没有找到解决方案。甚至没有调试它。

会话保存在定义的路径中。其ID与浏览器中的PHPSESSID相同。 网站运行HTTP协议。

security.yml

security:
    encoders:
        AppBundle\Entity\User:
            algorithm: bcrypt
            cost: 12

    providers:
        user_provider:
            entity:
                class: AppBundle:User
                property: email

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

        main:
            stateless: true
            pattern: ^/
            anonymous: true
            logout_on_user_change: true

            guard:
                authenticators:
                    - AppBundle\Security\LoginFormAuthenticator

            form_login:
                provider: user_provider
                username_parameter: email
                csrf_token_generator: security.csrf.token_manager
                login_path: app_login

            logout:
                path: app_logout


    access_control:
        - { path: ^/admin,roles: ROLE_ADMIN }
        - { path: ^/,roles: IS_AUTHENTICATED_ANONYMOUSLY }

路由:

app_login:
    path:     /login
    defaults: { _controller: AppBundle\Controller\BackendController:loginAction }

app_logout:
    path:     /logout
    defaults: { _controller: AppBundle\Controller\BackendController:logoutAction }

app_admin:
    path:         /admin/{page}/{entry}
    defaults:     { _controller: AppBundle\Controller\BackendController:showAction,entry: null }

User.php

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User implements UserInterface,\Serializable,EquatableInterface
{
    private $id;
    // and so on

    public function serialize()
    {
        return serialize(array(
            $this->id,$this->email,$this->password
        ));
    }

    public function unserialize($serialized)
    {
        list (
            $this->id,$this->password,) = unserialize($serialized);
    }

    public function getRoles()
    {
        return array('ROLE_ADMIN');
    }

    public function getUsername()
    {
        return $this->getEmail();
    }

    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof User) {
            return false;
        }

        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->salt !== $user->getSalt()) {
            return false;
        }

        if ($this->email !== $user->getUsername()) {
            return false;
        }

        return true;
    }

}

后端控制器:

class BackendController extends AbstractController
{
    public function loginAction(AuthenticationUtils $authenticationUtils)
    {
        return $this->render('AppBundle:Backend:page.html.twig',array(
            'email' => $authenticationUtils->getLastUsername(),'error' => $authenticationUtils->getLastAuthenticationError()
        ));
    }

    public function logoutAction()
    {
        $this->container->get('security.token_storage')->setToken(null);
        $this->container->get('request')->getSession()->invalidate();
    }

    public function showAction(Request $request,$page,$entry)
    {
        $this->denyAccessUnlessGranted('ROLE_ADMIN',null,'Unable to access this page!');

        // some logic
    }
}

LoginFormAuthentication.php

在示例中看起来相同,并且可以正常工作。它成功到达onAuthenticationSuccess()并重定向到管理区域。

dev.log

request.INFO: Matched route "app_login". {"route":"app_login"..}
security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
security.DEBUG: Calling getCredentials() on guard authenticator. {"firewall_key":"main","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
security.DEBUG: Passing guard token information to the GuardAuthenticationProvider {"firewall_key":"main","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
doctrine.DEBUG: SELECT t0.* FROM user t0 WHERE t0.email = ? LIMIT 1 ["email@me.com"] []
security.INFO: Guard authentication successful! {"token":"[object] (Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken: PostAuthenticationGuardToken(user=\"email@me.com\",authenticated=true,roles=\"ROLE_ADMIN\"))","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
security.DEBUG: Guard authenticator set success response. Redirect response
security.DEBUG: Remember me skipped: it is not configured for the firewall.
security.DEBUG: The "AppBundle\Security\LoginFormAuthenticator" authenticator set the response. Any later authenticator will not be called {"authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []

重定向后:

request.INFO: Matched route "app_admin". {"route":"app_admin" ..}
security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"AppBundle\\Security\\LoginFormAuthenticator"} []
security.INFO: Populated the TokenStorage with an anonymous Token. [] []
security.DEBUG: Access denied,the user is not fully authenticated; redirecting to authentication entry point.
security.DEBUG: Calling Authentication entry point. [] []

解决方法

您必须更改卫士身份验证器 AppBundle \ Security \ LoginFormAuthenticator

这向警卫人员说明,您只需要检查登录页面上的凭据即可

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

https://symfony.com/doc/4.4/security/guard_authentication.html#avoid-authenticating-the-browser-on-every-request

, 我的同事找出了问题所在。上面的代码实际上有多个问题。
  1. 使用GuardAuthenticator接口已从sf4中删除: https://github.com/symfony/symfony/blob/4.4/UPGRADE-4.0.md#security
  2. 无需logout_on_user_change
  3. 不需要LoginFormAuthenticator。
  4. 无状态:true是防火墙中的错误设置,但是当我删除它时,它将引发先前的错误:“由于用户已更改,无法刷新令牌。尝试刷新令牌后,令牌已取消身份验证。”发生了,因为
  5. 在isEqualTo中,我检查了$this->salt !== $user->getSalt(),但未序列化

因此工作解决方案如下

  • 路由相同
  • 后端控制器是相同的
  • LoginFormAuthentication.php已被删除

security.yml

security:
    encoders:
        AppBundle\Entity\User:
            algorithm: bcrypt
            cost: 12

    providers:
        user_provider:
            entity:
                class: AppBundle:User
                property: email

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

        main:
            anonymous: ~
            provider: user_provider

            form_login:
                login_path: app_login
                check_path: app_login
                default_target_path: app_admin

            logout:
                path: app_logout

    access_control:
        - { path: ^/admin,roles: ROLE_ADMIN }
        - { path: ^/,roles: IS_AUTHENTICATED_ANONYMOUSLY }

User.php

class User implements UserInterface,\Serializable,EquatableInterface
{

    // ..

    public function serialize()
    {
        return serialize(array(
            $this->id,$this->email,$this->password,$this->salt,));
    }

    public function unserialize($serialized)
    {
        list (
            $this->id,$this->salt
            ) = unserialize($serialized,array('allowed_classes' => false));
    }

    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof User) {
            return false;
        }

        if ($user->getId() == $this->getId()) {
            return true;
        }

        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->salt !== $user->getSalt()) {
            return false;
        }

        if ($this->email !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...