2011-01-16 35 views
14

Estoy trabajando en un pequeño proyecto respaldado por Doctrine2 que usa Symfony2 por primera vez. Actualmente estoy luchando con el componente de seguridad de symfony2, para ser exactos con el mecanismo de autenticación descrito en el documentation.Mecanismo de autenticación basado en Doctrine en el proyecto Symfony2

que desee utilizar una autenticación basada en formularios e hizo todo lo indicado en la documentación:

tengo un archivo de configuración security.yml que se parece a esto:

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 

El check_page se excluye de el área "sin seguridad" después de leer un hilo similar en devcomments.

En mi configuración de enrutamiento incluyo dos reglas para la autenticación:

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

_security_check: 
    pattern:      /validateLogin 

La clase de entidad que estoy utilizando para representar a un usuario es una entidad Doctrine2 e implementa el 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(); 
} 
} 

En la clase AuthController estoy usando el código de ejemplo de los documentos 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)); 
} 

Ahora viene el problema: la regla de redirección de http://symfony2.localhost/app_dev.php/admin/test a http://symfony2.localhost/app_dev.php/login funciona pero después de ingresar el nombre de usuario/contraseña y enviar el formulario de inicio de sesión, me redirigen a la url de inicio de sesión nuevamente sin un mensaje de error.

Sé que esto es probablemente un problema realmente básico, pero como aún no hay mucha documentación sobre Symfony2, creo que este es un buen lugar para hacer preguntas como esta. En general, hay algunos puntos dentro de un proyecto symfony2 que parecen funcionar de forma mágica (por supuesto, con respaldo DI) que dificultan el proceso de aprendizaje. Mi opinión sobre cómo funciona la autenticación es que hay algún Controlador mágico que captura la acción validateLogin, busca un repositorio de entidad para mi entidad de usuario, llama a findOneBy ('nombre de usuario' => $ nombre de usuario) y compara las contraseñas ... es esto ¿derecho?

Gracias de antemano por cualquier sugerencia, he estado buscando en Google este tema para más algunas horas ahora ... :)

Paul

Respuesta

20

Mis pensamientos sobre cómo funciona la autenticación es que hay algún controlador mágica que atrapa la acción validateLogin, busca un repositorio de entidad para mi entidad de usuario, las llamadas findOneBy ('nombre de usuario' => $ nombre de usuario) y compara la contraseñas ... ¿es esto correcto?

Estás equivocado. La autenticación no implica ningún controlador, por eso no especifica ninguna en la ruta _security_check. Auth se basa en EventDispatcher. Cada vez que especifique un oyente en su firewall (por ejemplo, form_login, anonymous, logout etc.), en realidad registra un nuevo oyente para el evento core.security. Symfony\Component\HttpKernel\Security\Firewall::handle() es un lugar donde estos oyentes están realmente registrados.

El general, de flujo simplificado:

  1. usuario rellena formulario de acceso (_username y _password campos).
  2. La solicitud es gestionada por Symfony2.
  3. core.security evento está activado.
  4. EventDispatcher notifica a todos los oyentes.
  5. UsernamePasswordFormAuthenticationListener se dispara (handle() método) y comprueba si:
    1. URL coincide check_path opción.
    2. La solicitud tiene los parámetros _username y _password.
  6. El oyente intenta autenticar al usuario (método attemptAuthentication()).
  7. Authentication Manager activa a todos los proveedores registrados.
  8. Finalmente, DaoAuthenticationProvider se activa y trata de recuperar al usuario utilizando la clase de repositorio de usuarios de Doctrine.
  9. Si todo está bien UsernamePasswordToken (que contiene $user objeto devuelto por el método loadUserByUsername()) se devuelve y se redirige al usuario.

De hecho, el mecanismo de seguridad es bastante complejo y difícil de entender (la documentación aún no está terminada). Pero cuando finalmente entiendas cómo funciona, verás qué poderoso mecanismo es.


Escribí mi propio mecanismo de autenticación y funciona bien.

  1. Configuración:

    estoy usando proveedor personalizado y un codificador.

    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 } 
    

    Como se puede ver /panel/* está restringido, mientras que /* es público.

  2. Servicio security.encoder.sha512 es un codificador incorporado:

    <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. rutas seguridad y control ler es igual que el tuyo.

+0

¡Guau, gracias por la respuesta! ¡Voy a intentar un honor más tarde! – Paul

4

Se debe utilizar la https://github.com/FriendsOfSymfony/FOSUserBundle FOS UserBundle, se pone en práctica todo esto con Doctrine 2 y tiene toneladas de características.

+0

gracias, he visto esto hace un tiempo. sin embargo, me alegraría saber qué está pasando allí ... parece que tengo que leer más código de Symfony :) – Paul

+1

Hay alguna información de configuración de cómo hacerlo para UserBundle [aquí] (https://github.com /FriendsOfSymfony/UserBundle/blob/master/Resources/doc/index.rst), pero me encantaría ver un tutorial que detalla cómo instalar el paquete hasta cómo obtener un formulario de inicio de sesión/suscripción. Mientras tanto, leeré, romperé las cosas y leeré más. – Banjer

1

La razón, en esencia, ¿por qué se carga la página de inicio de sesión de nuevo con ningún mensaje de error se debe a que, irónicamente, la configuración de seguridad no están configurados para permitir el acceso anónimo a la página de inicio de sesión.

Cuestiones relacionadas