2011-01-16 17 views
14

Sto lavorando per la prima volta su un piccolo progetto supportato da Doctrine2 utilizzando Symfony2. Attualmente sto lottando con il componente di sicurezza di symfony2, per essere precisi con il meccanismo di autenticazione descritto nello documentation.Meccanismo di autenticazione basato su Doctrine nel progetto Symfony2

voglio utilizzare l'autenticazione basata su form e ha fatto tutto dichiarato nella documentazione:

ho un file di configurazione security.yml che assomiglia a questo:

security.config: 
    firewalls: 
     admin: 
      pattern:        /admin/.* 
      form-login:       true 
      logout:        true 
      login_path:       /login 
      check_path:       /validateLogin 
      always_use_default_target_path:  false 
      target_path_parameter:    target 
     check_page: 
      pattern:        /validateLogin 
      form-login:       true 
      login_path:       /login 
      check_path:       /validateLogin 
      always_use_default_target_path:  false 
      target_path_parameter:    target 
     public: 
      pattern:        /.* 
      security:       false 
    providers: 
     admin: 
      password_encoder:     md5 
      entity: 
       class:       AdminBundle:User 
       property:      username 
    access_control: 
     - { path: /admin/.*, role: ROLE_ADMIN } 
     - { path: /validateLogin, role: IS_AUTHENTICATED_ANONYMOUSLY } 
    role_hierarchy: 
     ROLE_ADMIN:  ROLE_USER 

Il check_page è escluso dalla l'area "senza protezione" dopo aver letto un thread simile su devcomments.

Nella mia configurazione del routing includo due regole per l'autenticazione:

_security_login: 
    pattern:      /login 
    defaults:  
     _controller:    PublicBundle:Auth:index 

_security_check: 
    pattern:      /validateLogin 

La classe entità che sto usando per rappresentare un utente è un'entità Doctrine2 e implementa l'AccountInterface:

<?php 

namespace Application\AdminBundle\Entity; 

use Symfony\Component\Security\User\AccountInterface; 

/** 
* @orm:Entity 
*/ 
class User implements AccountInterface 
{ 
/** 
* @orm:Id 
* @orm:Column(type="integer") 
* @orm:GeneratedValue(strategy="IDENTITY") 
*/ 
protected $id; 
/** 
* @orm:Column(type="string", length="255") 
*/ 
protected $username; 
/** 
* @orm:Column(type="string", length="40") 
*/ 
protected $password; 

public function getId() 
{ 
    return $this->id; 
} 

public function setId($id) 
{ 
    $this->id = $id; 
} 

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

public function setUsername($username) 
{ 
    $this->username = $username; 
} 

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

public function setPassword($password) 
{ 
    $this->password = $password; 
} 

/** 
* Implementing the AccountInterface interface 
*/ 
public function __toString() 
{ 
    return $this->getUsername(); 
} 

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

public function eraseCredentials() 
{ 

} 

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

In la classe AuthController Sto usando il codice di esempio dai documenti symfony2:

public function indexAction() 
{ 
    if ($this->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) { 
     $error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR); 
    } else { 
     $error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR); 
    } 

    return 
     $this->render(
      'PublicBundle:Auth:index.twig', 
      array(
       'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME), 
       'error' => $error)); 
} 

Ora viene il problema: la regola di reindirizzamento da http://symfony2.localhost/app_dev.php/admin/test a http://symfony2.localhost/app_dev.php/login funziona ma, dopo aver immesso nome utente/password e inoltrato il modulo di accesso, vengono nuovamente reindirizzati all'URL di accesso senza un messaggio di errore.

So che questo è probabilmente un problema di base ma poiché non c'è ancora molta documentazione su symfony2, penso che questo sia un buon posto per fare domande come questa. In generale ci sono alcuni punti all'interno di un progetto symfony2 che sembrano funzionare magicamente (ovviamente con DI-backed) che rendono il processo di apprendimento un po 'difficile. Il mio pensiero su come funziona l'autenticazione è che c'è un controller magico che cattura l'azione validateLogin, cerca un repository di entità per la mia entità User, chiama findOneBy ('username' => $ username) e confronta le password ... è questo destra?

Grazie in anticipo per qualsiasi suggerimento, ho googling questo problema per più qualche ora ora ... :)

Paul

risposta

20

I miei pensieri su come funziona l'autenticazione è che v'è una certa controller magica che cattura l'azione validateLogin, cerca un repository di entità per la mia entità User, chiama findOneBy ('username' => $ username) e confronta il password ... è giusto?

Ti stai sbagliando. L'autenticazione non coinvolge alcun controller, ecco perché non si specifica alcuno nella route _security_check. L'autenticazione è basata su EventDispatcher. Ogni volta che si specifica un listener nel firewall (ad esempio form_login, anonymous, logout ecc.) Si registra effettivamente un nuovo listener per l'evento core.security. Symfony\Component\HttpKernel\Security\Firewall::handle() è un luogo in cui questi listener sono effettivamente registrati.

Il generale, di flusso semplificato:

  1. utente riempie form di login (_username e _password campi).
  2. La richiesta è gestita da Symfony2.
  3. core.security evento viene generato.
  4. EventDispatcher notifica tutti i listener.
  5. UsernamePasswordFormAuthenticationListener viene licenziato (handle() metodo) e verifica se:
    1. URL corrisponde check_path opzione.
    2. La richiesta ha entrambi i parametri _username e _password.
  6. Listener prova ad autenticare l'utente (attemptAuthentication() metodo).
  7. Il gestore autenticazione attiva tutti i provider registrati.
  8. Infine, DaoAuthenticationProvider viene generato e tenta di recuperare l'utente utilizzando la classe di repository utenti di Doctrine.
  9. Se tutto va bene, UsernamePasswordToken (che contiene l'oggetto) restituito e l'utente viene reindirizzato.

In effetti il ​​meccanismo di sicurezza è piuttosto complesso e difficile da comprendere (la documentazione non è ancora finita). Ma quando finalmente capisci come funziona, vedrai quanto potente sia il meccanismo.


Ho scritto il mio meccanismo di autenticazione e funziona correttamente.

  1. Configurazione:

    sto usando provider personalizzato ed encoder.

    security.config: 
        providers: 
         main: 
          id:   project.user_repository # DI id. Doctrine's UserRepositry 
          check_path: /login-check 
        encoders: 
         main: 
          class: Project\SiteBundle\Entity\User 
          id: security.encoder.sha512  # DI id. Service %security.encoder.digest.class% (with "sha512" as first parameter) 
        firewalls: 
         restricted: 
          pattern: /panel/.* 
          form_login: 
           check_path: /login-check 
         public: 
          pattern: /.* 
          anonymous: true 
          form_login: 
           check_path: /login-check 
          logout:  true 
        access_control: 
         - { path: /panel/.*, role: ROLE_USER } 
         - { path: /.*, role: IS_AUTHENTICATED_ANONYMOUSLY } 
    

    Come si può vedere /panel/* è limitato, mentre /* è pubblica.

  2. servizio security.encoder.sha512 è un encoder built-in:

    <service id="security.encoder.sha512" class="%security.encoder.digest.class%"> 
        <argument>sha512</argument> 
    </service> 
    
  3. Project\SiteBundle\Entity\User:

    /** 
    * @orm:Entity(repositoryClass="Project\SiteBundle\Repository\UserRepository") 
    */ 
    class User implements AdvancedAccountInterface { 
        /** 
        * @orm:Id @orm:Column(type="integer") 
        * @orm:GeneratedValue(strategy="AUTO") 
        */ 
        protected $id; 
    
        /** 
        * @orm:Column(unique=true, nullable=true) 
        */ 
        protected $email; 
    
        /** 
        * @orm:Column(unique=true, nullable=true) 
        */ 
        protected $xmpp; 
    
        /** 
        * @orm:Column(length=128) 
        */ 
        protected $password; 
    
        /** 
        * @orm:Column(length=16) 
        */ 
        protected $salt; 
    
        // User can be logged in using email address or xmpp adress. 
    
        // Dozens of getters/setters here. 
    } 
    
  4. Project\SiteBundle\Repository\UserRepository

    class UserRepository extends EntityRepository implements UserProviderInterface { 
        public function loadUserByUsername($username) { 
         $dql = sprintf(' 
          SELECT u 
          FROM %s u 
          WHERE u.email = :id OR u.xmpp = :id 
         ', $this->_entityName); 
    
         $user = null; 
    
         try { 
          $user = $this->_em->createQuery($dql)->setParameter('id', $username)->getSingleResult(); 
         } catch (ORMException $e) { 
          throw new UsernameNotFoundException("User $username not found.", $e->getCode(), $e); 
         } 
    
         return $user; 
        } 
    
        public function loadUserByAccount(AccountInterface $user) { 
         return $this->loadUserByUsername($user->getUsername()); 
        } 
    } 
    
  5. percorsi di sicurezza e controllo è uguale al tuo.

+0

Wow, grazie per la risposta! Cercherò un onore più tardi! – Paul

4

È consigliabile utilizzare il https://github.com/FriendsOfSymfony/FOSUserBundle FOS UserBundle, implementa tutto questo con Doctrine 2 e ha tonnellate di funzionalità.

+0

grazie, l'ho visto qualche tempo fa. tuttavia sarei lieto di sapere cosa sta succedendo lì ... sembra come se dovessi leggere qualche altro codice symfony :) – Paul

+1

Ci sono alcune istruzioni di impostazione how-to per UserBundle [qui] (https://github.com) /FriendsOfSymfony/UserBundle/blob/master/Resources/doc/index.rst), ma mi piacerebbe vedere un tutorial che spiega in dettaglio come installare il pacchetto fino in fondo su come ottenere un modulo di accesso/registrazione. Nel frattempo, leggerò, romperò le cose e leggerò ancora. – Banjer

1

La ragione, in sostanza, il motivo per cui la pagina di accesso carichi di nuovo senza alcun messaggio di errore è perché, ironia della sorte, le impostazioni di protezione non sono impostati per consentire l'accesso anonimo alla pagina di login.

Problemi correlati