登录后重定向

时间:2017-07-27 21:04:22

标签: symfony symfony-security

我目前正在使用Symfony的组件将旧的自定义框架移植到基于Symfony的自定义框架。到目前为止,一切都很顺利,除了登录部分。以下是该项目的一些细节:

  • 我正在使用Symfony Security Component v2.8
  • 我的会话使用PDOSessionHandler
  • 存储在数据库中
  • 我正在使用Guard对我的用户进行身份验证。

当用户尝试使用表单登录管理区域时,会出现问题。表单提交后,用户将转发到login_check路由,在该路由中成功检查所有凭据。设置用户角色ROLE_ADMIN,最后将用户重定向到安全页面,然后自动重定向回登录。事件的顺序如下:

登录 - > login_check - > admin - >登录

我通过在ContextListener :: OnKernelResponse中设置断点进行了一些调试,发现一个令牌永远不会保存在会话中,因为该方法在此处返回:

if (!$event->getRequest()->hasSession()) {
    return;
   }

此外,我能够看到一个会话被添加到数据库表中,并且会话ID在整个重定向中保持不变。最后我被退回到登录页面,我的用户被设置为 .anon / login_check和/ admin之间的某处我的令牌丢失了。

我已经没有关于如何调试它的想法。我正在粘贴一些代码以帮助我了解我的设置,但我认为这些都很好。

我的防火墙配置看起来像这样

return[
'security'=>[
    //Providers
    'providers'=>[
        'user' => array(
            'id' => 'security.user.provider.default',
        ),
    ],

    //Encoders
    'encoders'=>[
        'Library\\Security\\Users\\User::class' => array('algorithm' => 'bcrypt', 'cost'=> 15)
    ],
    'firewalls'=>
        [
            'backend'=>array(
                'security'       =>true,
                'anonymous'      => true,
                'pattern'        => '^/',
                'guard'          => array(
                    'authenticators'  => array(
                        'security.guard.form.authenticator',
                        'security.authenticator.token'
                    ),
                    'entry_point'=>'security.guard.form.authenticator'
                ),
            ),


        ],
    'access_control'=>array(
        array('path' => '^/admin', 'roles' => ['ROLE_ADMIN']),
        array('path' => '^/api', 'roles' => ['ROLE_API']),
        array('path' => '^/pos', 'roles' => ['ROLE_POS']),
        array('path' => '^/dashboard', 'roles' => ['ROLE_SUPER_ADMIN']),
        array('path' => '^/login', 'roles' => ['IS_AUTHENTICATED_ANONYMOUSLY']),
        array('path' => '/', 'roles' => ['IS_AUTHENTICATED_ANONYMOUSLY']),
    )

]];

我的 UserInterface

class User implements UserInterface, EquatableInterface{


 private $username;
    private $password;
    private $salt;
    private $roles;

public function __construct($username, $password, $salt, array $roles)
{
    $this->username = $username;
    $this->password = $password;
    $this->salt = $salt;
    $this->roles = $roles;
}

public function getRoles()
{
    return $this->roles;
}

public function getPassword()
{
    return $this->password;
}

public function getSalt()
{
    return $this->salt;
}

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

public function eraseCredentials()
{
}

public function isEqualTo(UserInterface $user)
{

    if (!$user instanceof DefaultUserProvider) {
        return false;
    }

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

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

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

    return true;
}}

我的 UserProvider

namespace Library\Security\UserProviders;

use Library\Nosh\Project\Project;
    use Library\Security\Users\User;
    use Symfony\Component\Security\Core\User\UserProviderInterface;
    use Symfony\Component\Security\Core\User\UserInterface;
    use PDO;
    use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;

    class DefaultUserProvider implements UserProviderInterface{
        private $db;

    private $project;

    public function __construct(\PDO $db, Project $project)
    {
        $this->db = $db;

        $this->project=$project;
    }

    public function loadUserByUsername($username)
    {
        $projectId = $this->project->id();

        $statement = $this->db->prepare("SELECT * FROM users WHERE :userLogin IN (user_login, user_email) AND project_id=:project_id  AND user_active=:user_active");

        $statement->bindParam(':userLogin', $username, PDO::PARAM_STR);
        $statement->bindValue(':user_active', 1, PDO::PARAM_INT);
        $statement->bindValue(':project_id', $projectId, PDO::PARAM_INT);
        $statement->execute();

        if (!$user = $statement->fetch()) {
            throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
        }

        $roles = explode(',', $user['user_roles']);

        return new User($user['user_login'], $user['user_password'],$salt='',$roles);
    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }

        return $this->loadUserByUsername($user->getUsername());
    }

    public function supportsClass($class)
    {
        return $class ===  'Library\\Security\\Users\\User';
    }

}

1 个答案:

答案 0 :(得分:0)

I was able to solve my own problem after several days of debugging. The reason I had no session is because I had failed to implement a SessionListener to store the session into the request. This should not be an issue for anyone using the Symfony Framework or Silex. It was only an issue for me, because I am actually creating something from scratch.

For anyone wondering how to do this, here are the necessary steps:

  • Create a class which extends Symfony\Component\HttpKernel\EventListener\SessionListener
  • Implement the method getSession()
  • Make sure you add the class to the dispatcher with addSubscriber()

See my example below:

SessionListener

use Symfony\Component\HttpKernel\EventListener\SessionListener as AbstractSessionListener;

class SessionListener extends AbstractSessionListener {

    private $container;

    public function __construct(Container $container)
    {
        $this->container=$container;
    }

    protected function getSession()
    {
        if (!$this->container->has('session')) {
            return;
        }
        return $this->container->get('session');
    }
}

SessionServiceProvider

use Core\Container;
use Interfaces\EventListenerProviderInterface;
use Interfaces\ServiceProviderInterface;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class SessionServiceProvider implements ServiceProviderInterface, EventListenerProviderInterface {

    protected $options=[
        'cookie_lifetime'=>2592000,//1 month
        'gc_probability'=>1,
        'gc_divisor'=>1000,
        'gc_maxlifetime'=>2592000
    ];

    public function register(Container $container){


        switch($container->getParameter('session_driver')){
            case 'database':
                $storage = new NativeSessionStorage($this->options, new PdoSessionHandler($container->get('db')));

                break;

            case 'file':
                $storage = new NativeSessionStorage($this->options, new NativeFileSessionHandler($container->getParameter('session_dir')));
                break;

            default:
                $storage = new NativeSessionStorage($this->options, new NativeFileSessionHandler($container->getParameter('session_dir')));
                break;
        }

        $container->register('session',Session::class)->setArguments([$storage]);


    }

    public function subscribe(Container $container, EventDispatcherInterface $dispatcher)
    {
        $dispatcher->addSubscriber(new SessionListener($container));
    }
}