2012-05-02 27 views
12

Estoy trabajando en una aplicación Symfony 2 donde el usuario debe seleccionar un perfil durante el proceso de inicio de sesión.Verificación de autenticación manual Symfony 2

Los usuarios pueden tener múltiples perfiles para trabajar y solo conocen sus propios perfiles. Entonces, primero necesito pedir el nombre de usuario y la contraseña, si son correctos, no debo iniciar sesión con el usuario, necesito solicitar el perfil que el usuario utilizará durante la sesión.

Entonces, muestro un formulario con un campo de nombre de usuario y contraseña, y lo envío usando una solicitud Ajax, esa solicitud responde con la lista de perfiles si el nombre de usuario y la contraseña son correctos o un código de error diferente. Finalmente, el usuario inicia sesión en el sistema usando nombre de usuario, contraseña y perfil.

El problema es que no sé cómo verificar si los datos de autenticación son correctos (utilizando todos mis administradores de autenticación, proveedores de usuarios, etc.) para realizar este paso intermedio (solicitudes de perfil) sin registrar realmente al usuario.

¿Alguien me puede ayudar con esto?

Respuesta

2

que utiliza el código de @Jordon y @Potor Polak para envolver la lógica de un servicio independiente que utiliza el token de acceso actual para validar la contraseña. Tal vez algunas de las necesidades esto:

services.yml:

app.validator.manual_password: 
    class: AppBundle\Service\ManualPasswordValidator 
    arguments: 
     - '@security.token_storage' 
     - '@security.encoder_factory' 

ManualPasswordValidator.php:

<?php 

namespace AppBundle\Service; 

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; 
use Symfony\Component\Security\Core\Encoder\EncoderFactory; 

/** 
* Class ManualPasswordValidator 
* 
* @package AppBundle\Service 
*/ 
class ManualPasswordValidator 
{ 
    /** 
    * @var EncoderFactory 
    */ 
    protected $encoderFactory; 

    /** 
    * @var TokenStorage 
    */ 
    protected $tokenStorage; 

    /** 
    * ManualPasswordValidator constructor. 
    * 
    * @param EncoderFactory $encoderFactory 
    * @param TokenStorage $tokenStorage 
    */ 
    public function __construct(TokenStorage $tokenStorage, EncoderFactory $encoderFactory) 
    { 
     $this->encoderFactory = $encoderFactory; 
     $this->tokenStorage = $tokenStorage; 
    } 

    /** 
    * @param $password 
    * @return bool 
    */ 
    public function passwordIsValidForCurrentUser($password) 
    { 
     $token = $this->tokenStorage->getToken(); 

     if ($token) { 
      $user = $token->getUser(); 

      if ($user) { 
       $encoder = $this->encoderFactory->getEncoder($user); 

       if ($encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) { 
        return true; 
       } 
      } 
     } 

     return false; 
    } 
} 

Después de esto puede inyectar el ManualPasswordValidator donde quiera y usarla como:

$password  = $request->get('password'); 
$passwordIsValid = $this->manualPasswordValidator->passwordIsValidForCurrentUser($password); 
16

Se podría hacer algo como esto para recuperar el usuario y probar manualmente la contraseña -

$username = trim($this->getRequest()->query->get('username')); 
$password = trim($this->getRequest()->query->get('password')); 

$em = $this->get('doctrine')->getEntityManager(); 
$query = $em->createQuery("SELECT u FROM \Some\Bundle\Entity\User u WHERE u.username = :username"); 
$query->setParameter('username', $username); 
$user = $query->getOneOrNullResult(); 

if ($user) { 
    // Get the encoder for the users password 
    $encoder_service = $this->get('security.encoder_factory'); 
    $encoder = $encoder_service->getEncoder($user); 
    $encoded_pass = $encoder->encodePassword($password, $user->getSalt()); 

    if ($user->getPassword() == $encoded_pass) { 
    // Get profile list 
    } else { 
    // Password bad 
    } 
} else { 
    // Username bad 
} 

Una vez que tenga su perfil de vuelta desde el cliente, puede realizar la conexión manualmente en el controlador del servidor AJAX con bastante facilidad también -

// Get the security firewall name, login 
$providerKey = $this->container->getParameter('fos_user.firewall_name'); 
$token = new UsernamePasswordToken($user, $password, $providerKey, $user->getRoles()); 
$this->get("security.context")->setToken($token); 

// Fire the login event 
$event = new InteractiveLoginEvent($this->getRequest(), $token); 
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event); 

puede ser que necesite un par de líneas de uso -

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; 
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; 
+1

Creo firmemente asesorar a mirar solución @Piotr Polak ya que funciona con todos l codificadores de contraseña. –

+0

No edite la respuesta para cambiarlo por completo. En cambio, votó la respuesta correcta. – Alsciende

1

La única manera en que pude autenticar a mis usuarios en un controlador es haciendo una subpetición y luego redirigiendo. Aquí está mi código, yo estoy usando silex pero se puede adaptar fácilmente a Symfony2:

$subRequest = Request::create($app['url_generator']->generate('login_check'), 'POST', array('_username' => $email, '_password' => $password, $request->cookies->all(), array(), $request->server->all()); 

$response = $app->handle($subRequest, HttpKernelInterface::MASTER_REQUEST, false); 

return $app->redirect($app['url_generator']->generate('curriculos.editar')); 
30

Un problema con @ código de Jordan es que no funciona con algoritmos hash que generan diferentes valores hash de la misma contraseña (como bcrypt que cuenta internamente sus parámetros, tanto el número de iteraciones como el sal). Es más correcto usar isPasswordValid of the Encoder para comparar contraseñas.

Este es el código mejorado que funciona bien con bcrypt:

$username = trim($this->getRequest()->query->get('username')); 
$password = trim($this->getRequest()->query->get('password')); 

$em = $this->get('doctrine')->getManager(); 
$query = $em->createQuery("SELECT u FROM \Some\Bundle\Entity\User u WHERE u.username = :username"); 
$query->setParameter('username', $username); 
$user = $query->getOneOrNullResult(); 

if ($user) { 
    // Get the encoder for the users password 
    $encoder_service = $this->get('security.encoder_factory'); 
    $encoder = $encoder_service->getEncoder($user); 

    // Note the difference 
    if ($encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) { 
    // Get profile list 
    } else { 
    // Password bad 
    } 
} else { 
    // Username bad 
}