在onAuthenticationSuccess重定向后,Symfony用户丢失了

时间:2019-12-27 03:52:53

标签: php symfony authentication

已按照以下说明进行操作:https://symfony.com/doc/current/security/form_login_setup.html,并在第3步(在onAuthenticationSuccess方法中)-如果我将Exception保留在外,探查器栏将显示用户登录,但是如果我注释在重定向中,用户将在下一页上丢失。会话已建立并作为pdo工作。

有人有什么想法吗?

LoginFormAuthenticator.php

<?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\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Http\Util\TargetPathTrait;

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

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

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

    public function supports(Request $request)
    {
        return 'app_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)
    {
        // Check the user's password or other credentials and return true or false
        // If there are no credentials to check, you can just return true
        return true;
        //throw new \Exception('TODO: check the credentials inside '.__FILE__);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }
        //throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
        return new RedirectResponse($this->urlGenerator->generate('app_dashboard'));

    }

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

DashboardController.php

<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class DashboardController extends AbstractController
{
    private $session;
    function __construct(SessionInterface $session)
    {
        $this->session = $session;
    }
    /**
     * @Route("/dashboard", name="app_dashboard")
     */ 
    function dashboard()
    {
        return $this->render('account/dashboard.html.twig', []);
    }
}

security.yaml

security:
    providers:
        users:
            entity:
                class: 'App\Entity\User'
                property: 'email'
    encoders:
        App\Entity\User:
            algorithm: 'auto'
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: lazy
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator
            logout:
                path: app_logout
    access_control:
        - { path: ^/dashboard, roles: ROLE_USER }

User.php

<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository") 
 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 */
class User implements UserInterface, \Serializable 
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    /**
     * @var string
     *
     * @ORM\Column(name="email", type="string", length=2048, nullable=true)
     */
    private $email;
    /**
     * @var string
     *
     * @ORM\Column(name="password", type="string", length=4096, nullable=false)
     */
    private $password;
    /**
     * @var string
     *
     * @ORM\Column(name="salt", type="string", length=2048, nullable=true)
     */
    private $salt;
    ////////
    public function getId(): ?int
    {
        return $this->id;
    }
    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;
        return $this;
    }
    public function getPassword(): ?string
    {
        return $this->password;
    }
    public function setPassword(string $password): self
    {
        $this->password = $password;
        return $this;
    }
    public function getSalt(): ?string
    {
        return $this->salt;
    }
    public function setSalt(string $salt): self
    {
        $this->salt = $salt;
        return $this;
    }

    /**
     * @inheritDoc
     */
    public function getUsername()
    {
        return $this->email;
    }
    /**
     * @inheritDoc
     */
    public function getRoles()
    {
        return array('ROLE_USER');
    }
    /**
     * @inheritDoc
     */
    public function eraseCredentials()
    {
    }
    /**
     * @inheritDoc
     */
    public function equals(UserInterface $user)
    {
        return $this->id === $user->getId();
    }
    /**
     * @see \Serializable::serialize()
     */
    public function serialize()
    {
        return serialize(array(
            $this->id,
        ));
    }
    /**
     * @see \Serializable::unserialize()
     */
    public function unserialize($serialized)
    {
        list (
            $this->id,
        ) = unserialize($serialized);
    }
    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->getEmail()) {
            return false;
        }
        return true;
    }
}

编辑.. 当注释异常时,日志显示:

INFO
19:32:37
request Matched route "app_login".
Show context
INFO
19:32:38
security    Guard authentication successful!
Show context
CRITICAL
19:32:38
request Uncaught PHP Exception Exception: "TODO: provide a valid redirect inside /var/www/src/Security/LoginFormAuthenticator.php" at /var/www/src/Security/LoginFormAuthenticator.php line 87

当重定向存在时,日志说:

19:30:24
security    Checking for guard authentication credentials.
Hide context
[▼
  "firewall_key" => "main"
  "authenticators" => 1
]
19:30:24
security    Checking support on guard authenticator.
Hide context
[▼
  "firewall_key" => "main"
  "authenticator" => "App\Security\LoginFormAuthenticator"
]
19:30:24
security    Guard authenticator does not support the request.
Hide context
[▼
  "firewall_key" => "main"
  "authenticator" => "App\Security\LoginFormAuthenticator"
]

.. edit-http和https中的行为相同。

2 个答案:

答案 0 :(得分:0)

我想为您发表评论,但我的声誉不到50分,所以我将在评论中写下评论。

不建议使用支持方法。 不再使用它。

使用以下示例:

LoginFormAuthenticator.php

public function getCredentials(Request $request)
{
    $isLoginSubmit = 'user_login' === $request->attributes->get('_route') && $request->isMethod('POST');
    if(!$isLoginSubmit) {
        return;
    }

    //...other codes
}
protected function getDefaultSuccessRedirectUrl()
{
    return $this->urlGenerator->generate('user_dashboard');
}

UserController.php

<?php

namespace AppBundle\Controller\User;

use AppBundle\Form\User\UserManager\UserLoginForm;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

/**
 * User controller.
 *
 * @Route("User")
 */
class UserController extends Controller
{

    /**
     * @Route("/login", name="user_login")
     */
    public function loginAction()
    {
        $authenticationUtils = $this->get('security.authentication_utils');
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        $form = $this->createForm(UserLoginForm::class, ["_username" => $lastUsername]);

        return $this->render('User/login.html.twig', [
            'form'  => $form->createView(),
            'error' => $error,
        ]);
    }

    /**
     * @Route("/logout", name="user_logout")
     */
    public function logoutAction()
    {
        throw new \Exception('User logout');
    }

    /**
     * @Route("/dashboard", name="user_dashboard")
     */
    public function dashboardAction()
    {
        return $this->render('User/dashboard.html.twig');
    }
}

security.yaml

需要提供者和模式:

firewalls:
    //...other firewalls
    main:
        anonymous: ~
        pattern: ^/user
        provider: users

access_control:
    - { path: ^/user/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
    - { path: ^/user, roles: ROLE_USER }

希望能为您提供帮助。

留下任何反馈意见。

答案 1 :(得分:0)

问题是没有刷新用户,Guard Authenticator确实在做应有的事情。将用户实体还原到以下固定的实体。

<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity(fields={"email"}, message="There is already an account with this email")
 */
class User implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     */
    private $email;
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $password;
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name;
    public function getId(): ?int
    {
        return $this->id;
    }
    public function getEmail(): ?string
    {
        return $this->email;
    }
    public function setEmail(string $email): self
    {
        $this->email = $email;
        return $this;
    }
    public function getUsername()
    {
        return $this->email;
    }
    public function getPassword(): ?string
    {
        return $this->password;
    }
    public function setPassword(string $password): self
    {
        $this->password = $password;
        return $this;
    }
    public function getName(): ?string
    {
        return $this->name;
    }
    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }
    ////
    public function getRoles()
    {
        return [
            'ROLE_USER'
        ];
    }
    public function getSalt()
    {  
    }
    public function eraseCredentials()
    {
    }
}