簡體   English   中英

登錄后,Symfony 2.8會話丟失。

[英]Symfony 2.8 session lost after login_check redirect

我目前正在使用Symfony的組件將舊的自定義框架移植到基於Symfony的自定義框架。 到目前為止,除了登錄部分之外,其他所有事情都進行得很順利。 以下是有關該項目的一些詳細信息:

  • 我正在使用Symfony安全組件v2.8
  • 我的會話使用PDOSessionHandler存儲在數據庫中
  • 我正在使用Guard來驗證我的用戶。

當用戶嘗試使用表單登錄到管理區域時,就會出現問題。 提交表單后,用戶將被轉發到login_check路由,在此路由中成功檢查了所有憑據。 設置了用戶角色ROLE_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']),
    )

]];

我的用戶界面

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';
    }

}

經過幾天的調試,我能夠解決自己的問題。 我沒有會話的原因是因為我無法實現SessionListener來將會話存儲到請求中。 對於使用Symfony Framework或Silex的任何人來說,這都不是問題。 這對我來說只是一個問題,因為我實際上是從頭開始創建一些東西。

對於想知道如何執行此操作的任何人,以下是必要的步驟:

  • 創建一個擴展Symfony \\ Component \\ HttpKernel \\ EventListener \\ SessionListener的類
  • 實現方法getSession()
  • 確保使用addSubscriber()將類添加到調度程序中

請參閱下面的示例:

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));
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM