2014-12-07 119 views
2

我開始使用最新的穩定版本(2.6.1)開始一個新的Symfony2項目,並且我已經設置了最小的登錄和安全組件配置。但是,在處理/ login_check請求後,當用戶重定向回主頁時,一半的登錄嘗試似乎失敗。Symfony 2.6登錄隨機失敗

這就是日誌登錄失敗後顯示(箭頭雷強調):

request.INFO: Matched route "login_check" (parameters: "_route": "login_check") [] [] 
doctrine.DEBUG: SELECT t0.id AS id1, t0.username AS username2, t0.password AS password3, t0.nombre AS nombre4, t0.apellidos AS apellidos5, t0.email AS email6, t0.movil AS movil7, t0.ciudad AS ciudad8, t0.hospital AS hospital9, t0.cargo AS cargo10, t0.roles AS roles11, t0.created_at AS created_at12 FROM usuarios t0 WHERE t0.username = ? LIMIT 1 ["admin"] [] 
--> security.INFO: User "admin" has been authenticated successfully [] [] 
security.DEBUG: Write SecurityContext in the session [] [] 
request.INFO: Matched route "homepage" (parameters: "_controller": "AppBundle\Controller\DefaultController::indexAction", "_route": "homepage") [] [] 
--> security.INFO: Populated SecurityContext with an anonymous Token [] [] 
security.DEBUG: Write SecurityContext in the session [] [] 

正如你可以看到login_check過程實際上是在認證用戶成功,但是當它被重定向到另一個頁面系統以某種方式「忘記」它,就好像它實際上沒有將結果存儲在會話中一樣。當整個過程的工作日誌是這樣看:

request.INFO: Matched route "login_check" (parameters: "_route": "login_check") [] [] 
doctrine.DEBUG: SELECT t0.id AS id1, t0.username AS username2, t0.password AS password3, t0.nombre AS nombre4, t0.apellidos AS apellidos5, t0.email AS email6, t0.movil AS movil7, t0.ciudad AS ciudad8, t0.hospital AS hospital9, t0.cargo AS cargo10, t0.roles AS roles11, t0.created_at AS created_at12 FROM usuarios t0 WHERE t0.username = ? LIMIT 1 ["admin"] [] 
--> security.INFO: User "admin" has been authenticated successfully [] [] 
security.DEBUG: Write SecurityContext in the session [] [] 
request.INFO: Matched route "homepage" (parameters: "_controller": "AppBundle\Controller\DefaultController::indexAction", "_route": "homepage") [] [] 
--> security.DEBUG: Read SecurityContext from the session [] [] 
security.DEBUG: Reloading user from user provider. [] [] 
doctrine.DEBUG: SELECT t0.id AS id1, t0.username AS username2, t0.password AS password3, t0.nombre AS nombre4, t0.apellidos AS apellidos5, t0.email AS email6, t0.movil AS movil7, t0.ciudad AS ciudad8, t0.hospital AS hospital9, t0.cargo AS cargo10, t0.roles AS roles11, t0.created_at AS created_at12 FROM usuarios t0 WHERE t0.id = ? [1] [] 
--> security.DEBUG: Username "admin" was reloaded from user provider. [] [] 
security.DEBUG: Write SecurityContext in the session [] [] 

這是整個安裝我的時刻。我打賭它是在security.yml文件中配置錯誤或者錯誤(?),但我不知道還有什麼地方需要注意。

security.yml

security: 
    encoders: 
     AppBundle\Entity\Usuario: bcrypt 

    role_hierarchy: 
     ROLE_ADMIN:  ROLE_USER 
     ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] 

    providers: 
     usuarios: 
      entity: { class: AppBundle\Entity\Usuario, property: username } 

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

     default: 
      pattern: ^/ 
      anonymous: ~ 
      form_login: 
       check_path: login_check 
       login_path: login 
      logout: 
       path: logout 
       target: homepage 

    access_control: 
     - { path: ^/$,  roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     - { path: ^/.*,  roles: ROLE_USER } 

的routing.yml

app: 
    resource: @AppBundle/Controller/ 
    type:  annotation 

login_check: 
    path: /login_check 

logout: 
    path: /logout 

login.html.twig

{% extends 'base.html.twig' %} 

{% block body %} 
    {% if error %} 
     <div>{{ error.message }}</div> 
    {% endif %} 

    <form action="{{ path('login_check') }}" method="post"> 
     <label for="username">Nombre de usuario:</label> 
     <input type="text" id="username" name="_username" value="{{ last_username }}" /> 
     <label for="password">Contraseña:</label> 
     <input type="password" id="password" name="_password" /> 
     <input type="submit" name="login" value="Acceder" /> 
    </form> 
{% endblock %} 

DefaultController.php

<?php 

namespace AppBundle\Controller; 

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Symfony\Bundle\FrameworkBundle\Controller\Controller; 

class DefaultController extends Controller 
{ 
    /** 
    * @Route("/", name="homepage") 
    */ 
    public function indexAction() 
    { 
     // Symfony dummy template that just shows 'Homepage.' 
     return $this->render('default/index.html.twig'); 
    } 

    /** 
    * @Route("/login", name="login") 
    */ 
    public function loginAction() 
    { 
     $authUtils = $this->get('security.authentication_utils'); 

     return $this->render('login/index.html.twig', array(
      'last_username' => $authUtils->getLastUsername(), 
      'error'   => $authUtils->getLastAuthenticationError(), 
     )); 
    } 
} 

Usuario.php

<?php 

namespace AppBundle\Entity; 

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

/** 
* @ORM\Table(name="usuarios") 
* @ORM\Entity 
*/ 
class Usuario implements AdvancedUserInterface 
{ 
    const ROLE_USER = 'ROLE_USER'; 
    const ROLE_ADMIN = 'ROLE_ADMIN'; 

    /** 
    * @var integer 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="username", type="string", length=255, unique=true) 
    */ 
    private $username; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="password", type="string", length=60) 
    */ 
    private $password; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="nombre", type="string", length=255) 
    */ 
    private $nombre; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="apellidos", type="string", length=255) 
    */ 
    private $apellidos; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="email", type="string", length=255, unique=true) 
    */ 
    private $email; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="movil", type="string", length=255) 
    */ 
    private $movil; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="ciudad", type="string", length=255) 
    */ 
    private $ciudad; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="hospital", type="string", length=255) 
    */ 
    private $hospital; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="cargo", type="string", length=255) 
    */ 
    private $cargo; 

    /** 
    * @var string 
    * 
    * @ORM\Column(name="roles", type="simple_array", length=255) 
    */ 
    private $roles; 

    /** 
    * @var \DateTime 
    * 
    * @ORM\Column(name="created_at", type="datetime") 
    */ 
    private $createdAt; 

    public function __construct() 
    { 
     $this->roles = [self::ROLE_USER]; 
     $this->createdAt = new \DateTime(); 
    } 

    /** 
    * Get id 
    * 
    * @return integer 
    */ 
    public function getId() 
    { 
     return $this->id; 
    } 

    // ... An awful lot of generic getters and setters 

    // Plus the mandatory AdvancedUserInterface methods: 
    /** 
    * @inheritdoc 
    */ 
    public function getUsername() 
    { 
     return $this->username; 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function getPassword() 
    { 
     return $this->password; 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function isAccountNonExpired() 
    { 
     return true; 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function isAccountNonLocked() 
    { 
     return true; 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function isCredentialsNonExpired() 
    { 
     return true; 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function isEnabled() 
    { 
     return true; 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function getSalt() 
    { 
     return null; // I'm using the bcrypt encoder 
    } 

    /** 
    * @inheritdoc 
    */ 
    public function eraseCredentials() 
    { 
     $this->password = null; 
    } 
} 

回答

3

嗯,這是一個微妙的。我沒有提到我也在使用PDOSessionHandler將會話存儲在數據庫中,事實證明這是問題的根源。

到目前爲止,我曾經爲自己的主包中的Entity命名空間添加一個自定義Session類,以便在運行console doctrine:schema:create時生成會話表以及其他人。顯然,該表的強制模式在2.6版本中已被修改,我的實體不再匹配它。

https://github.com/symfony/symfony/issues/12833

幸好我還了解到,PDOSessionHandler類有一個名爲createTable()一個輔助方法,它正是這樣做的,因爲你必須把它定義爲一個服務反正我決定放棄實體的做法,並開始使用一個使用這種方法的命令。有了這個新模式,登錄過程現在總是有效。這裏的所有相關代碼和配置:

config.yml

framework: 
    session: 
     handler_id: session.handler.pdo 

services: 
    pdo: 
     class: PDO 
     arguments: 
      - "mysql:host=%database_host%;port=%database_port%;dbname=%database_name%" 
      - "%database_user%" 
      - "%database_password%" 
     calls: 
      - [setAttribute, [3, 2]] # \PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION 

    session.handler.pdo: 
     class:  Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler 
     arguments: ["@pdo"] 

SetupSessionsTableCommand.php

<?php 

namespace AppBundle\Command; 

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 
use Symfony\Component\Console\Input\InputInterface; 
use Symfony\Component\Console\Output\OutputInterface; 

/** 
* This command leverages the PdoSessionHandler::createTable() 
* method from the session.handler.pdo service. 
*/ 
class SetupSessionsTableCommand extends ContainerAwareCommand 
{ 
    protected function configure() 
    { 
     $this 
      ->setName('setup:sessions') 
      ->setDescription('Creates the sessions table'); 
    } 

    protected function execute(InputInterface $input, OutputInterface $output) 
    { 
     $sessionHandler = $this->getContainer()->get('session.handler.pdo'); 

     try { 
      $sessionHandler->createTable(); 
     } catch (\Exception $e) { 
      $output->writeln("<error>Exception thrown while attempting to create the 'sessions' table</error>"); 
      $output->writeln($e->getMessage()); 
      $output->writeln($e->getTraceAsString()); 

      return -1; 
     } 

     $output->writeln("<info>'sessions' table created successfully</info>"); 

     return 0; 
    } 
}