2012-01-22 13 views
8

Qualcuno potrebbe spiegare come è possibile creare manualmente un cookie di ricordami in un controller?Creare symfony2 ricordami cookie manualmente (FOSUserBundle)

Desidero che gli utenti rimangano connessi dopo aver premuto il pulsante "registrati" , senza dover accedere successivamente con le credenziali.

Ho provato a creare un cookie manualmente ma credo che il valore del cookie non sia corretto, e quindi la funzionalità "remember me" non funziona. Viene impostato un cookie con il nome corretto. L'ho verificato

La funzione remember me funziona come previsto quando si utilizza la normale procedura di accesso con le credenziali dell'utente.

security.yml security.yml ricordati di me

security: 
    firewalls: 
     main: 
      remember_me: 
       lifetime: 86400 
       domain: ~ 
       path: /
       key:  myKey 

Questo è quello che ho adesso, anche se il cookie è impostato, non funziona.

$um = $this->get('fos_user.user_manager'); 
$member = $um->createUser(); 

… Form stuff with bindRequest etc. 

$um->updatePassword($member); 
$um->updateUser($member); 

$providerKey = $this->container->getParameter('fos_user.firewall_name'); 
$securityKey = 'myKey'; 

$token = new RememberMeToken($member, $providerKey, $securityKey, 
$member->getRoles()); 
$this->container->get('security.context')->setToken($token); 

$redirectResponse = new RedirectResponse($url); 
$redirectResponse->headers->setCookie(
    new \Symfony\Component\HttpFoundation\Cookie(
     'REMEMBERME', 
     base64_encode(implode(':', array($member->getUsername(), 
$member->getPassword()))), 
     time() + 60*60*24 
    ) 
); 
return $redirectResponse; 

Aggiornamento:

Ho anche provato a lavorare con la classe PersistentTokenBasedRememberMeServices con la riflessione, ma non funziona. un cookie viene impostato ma non funziona

$token = $this->container->get('security.context')->getToken(); 

$providerKey = $this->container->getParameter('fos_user.firewall_name'); 
$securityKey = 'myKey'; 

$persistenService = new 
PersistentTokenBasedRememberMeServices(array($um), $providerKey, 
$securityKey, array('path' => '/', 'name' => 'REMEMBERME', 'domain' => 
null, 'secure' => false, 'httponly' => true, 
'lifetime' => 86400)); 
$persistenService->setTokenProvider(new InMemoryTokenProvider()); 

$method = new \ReflectionMethod('Symfony\Component\Security\Http\RememberMe\PersistentTokenBasedRememberMeServices', 
'onLoginSuccess'); 
$method->setAccessible(true); 
$method->invoke($persistenService, $request, $redirectResponse, $token); 

sto utilizzando Symfony v2.0.5 e FOSUserBundle 1,0

UPDATE 2:

Ho provato un terzo modo. Lo stesso come sopra, ma senza riflessione:

$token = $this->container->get('security.context')->getToken(); 

$providerKey = $this->container->getParameter('fos_user.firewall_name'); 
$securityKey = 'myKey'; 

$persistenService = new PersistentTokenBasedRememberMeServices(array($um), $providerKey, $securityKey, array('path' => '/', 'name' => 'REMEMBERME', 'domain' => null, 'secure' => false, 'httponly' => true, 'lifetime' => 31536000, 'always_remember_me' => true, 'remember_me_parameter' => '_remember_me')); 
$persistenService->setTokenProvider(new InMemoryTokenProvider()); 

$persistenService->loginSuccess($request, $redirectResponse, $token); 

risposta

10

se si sta impostando direttamente il cookie RememberMe, è necessario utilizzare il seguente formato:

base64_encode(<classname>:base64_encode(<username>):<expiry-timestamp>:<hash>) 

dove l'hash sarà:

sha256(<classname> . <username> . <expiry-timestamp> . <password> . <key>) 

la chiave è la chiave inserita nella sicurezza (.xml/.yml) nella sezione remember_me.

Questo è preso da processAutoLoginCookie() metodo nella Symfony/Component/Sicurezza/HTTP/RememberMe/TokenBasedRememberMeService.php file.

Questo è tutto fatto dal metodo generateCookieValue() nella stessa classe.

Tuttavia, non consiglierei di utilizzarlo in questo modo direttamente, ma provare a vedere se è possibile chiamare il metodo TokenBasedRememberMeService::onLoginSuccess(), che imposta questo cookie per rendere il codice più robusto e portatile.

12

Ecco come l'ho fatto. Non sto usando FOSUserBundle e sto usando il provider di utenti di Enttrass di Doctrine, ma dovrebbe essere banale adattarsi alle tue esigenze.Ecco una soluzione generale:

// after registration and persisting the user object to DB, I'm logging the user in automatically 
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles()); 

// but you can also get the token directly, if you're user is already logged in 
$token = $this->container->get('security.context')->getToken(); 

// write cookie for persistent session storing 
$providerKey = 'main'; // defined in security.yml 
$securityKey = 'MySecret'; // defined in security.yml 

$userProvider = new EntityUserProvider($this->getDoctrine()->getEntityManager(), 'MyCompany\MyBundle\Entity\User', 'username'); 

$rememberMeService = new TokenBasedRememberMeServices(array($userProvider), $securityKey, $providerKey, array(
       'path' => '/', 
       'name' => 'MyRememberMeCookie', 
       'domain' => null, 
       'secure' => false, 
       'httponly' => true, 
       'lifetime' => 1209600, // 14 days 
       'always_remember_me' => true, 
       'remember_me_parameter' => '_remember_me') 
      ); 

$response = new Response(); 
$rememberMeService->loginSuccess($request, $response, $token); 

// further modify the response 
// ........ 

return $response; 

Basta ricordare quello che dovete impostare a true (come ho fatto nel codice sopra) o averlo tra le $ _POST parametri in qualche modo, in caso contrario il metodo isRememberMeRequested di AbstractRememberMeServices torneranno falso e il cookie non verrà memorizzato.

Si erano abbastanza vicino alla soluzione corretta però :) Quello che hai fatto di sbagliato (nel 3 ° tentativo) è che avete modificato l'ordine dei parametri qui:

$persistenService = new PersistentTokenBasedRememberMeServices(array($um), $providerKey, $securityKey, array('path' => '/', 'name' => 'REMEMBERME', 'domain' => null, 'secure' => false, 'httponly' => true, 'lifetime' => 31536000, 'always_remember_me' => true, 'remember_me_parameter' => '_remember_me')); 

Date un'occhiata a __construct() in AbstractRememberMeServices.php. Dovresti passare un 2 ° argomento e come terzo argomento, non viceversa come hai fatto per errore;)

Quello che non so ancora, è come ottenere parametri da security.yml direttamente nel controller per non duplicarlo. Usando $this->container->getParameter() posso ottenere parametri memorizzati sotto la chiave parameters in config.yml, ma non quelli posti più in alto nell'albero di configurazione. Qualche idea su questo?

+1

Per ottenere i parametri dei cookie in security.yml ed evitare duplicati, li si dovrebbe mettere in parameters.yml e nella sicurezza .yml chiamali con% your_parameter_name%. Sono ora disponibili in sicurezza e nei parametri – guillaumepotier

+3

+1 per l'utilizzo delle classi di sicurezza Symfony2 per questo. Tieni presente che il nome del cookie predefinito per i cookie remember-me è "REMEMBERME". Solo se si utilizza lo stesso nome di cookie del firewall che si sta autenticando con il proprio set di manuali, il cookie verrà riconosciuto dal listener di sicurezza. – flu

+1

L'argomento 1 passato a Symfony \ Bridge \ Doctrine \ Security \ User \ EntityUserProvider :: __ construct() deve implementare l'interfaccia Doctrine \ Common \ Persistence \ ManagerRegistry, istanza di Doctrine \ ORM \ EntityManager dato che ho ottenuto D: –

0

Per me la soluzione più semplice era estendere un BaseTokenBasedRememberMeServices e lasciarlo gestire

namespace AppBundke\Security\Http; 
use Symfony\Component\HttpFoundation\Cookie; 
use Symfony\Component\Security\Http\RememberMe\TokenBasedRememberMeServices as BaseTokenBasedRememberMeServices; 


class TokenBasedRememberMeServices extends BaseTokenBasedRememberMeServices 
{ 
    protected $options_new = array('name' => 'REMEMBERME', 'domain' => null, 'path' => '/'); 

    public function __construct($userProvider, $secret, $providerKey, array $options = array(), LoggerInterface $logger = null) 
    { 
      return parent::__construct(array($userProvider), $secret, $providerKey, array_merge($this->options_new, $options)); 
    } 

    public function generateCookie($user, $username, $expires, $password) 
    { 
     $cookie = new Cookie(
      $this->options['name'], 
      parent::generateCookieValue(get_class($user), $username, $expires, $password), 
      $expires, 
      $this->options['path'], 
      $this->options['domain'], 
      $this->options['secure'], 
      $this->options['httponly'] 
     ); 
    return $cookie; 
    } 
} 

e controller;

$user = $this->getUser(); 
$providerKey = $this->getParameter('fos_user.firewall_name'); 
$secret = $this->getParameter('secret'); 
$cookie_life_time = $this->getParameter('cookie_life_time'); 

$remember_me_service = new TokenBasedRememberMeServices($user, $secret, $providerKey); 
$remember_me_cookie = $remember_me_service->generateCookie($user, $user->getUsername(),(time() + $cookie_life_time), $user->getPassword()); 

quindi di risposta impostato dei cookie per $ remember_me_cookie

spero le sue opere con voi 2.

0

Ho avuto lo stesso problema quando ho cercato di impostare dei cookie REMEMBERME di un utente dopo la connessione con gettone, utilizzando Guard Authentication.

In questa situazione non disponevo di un oggetto Response per poter utilizzare $ response-> headers-> setCookie() e deve utilizzare setcookie(). E in questa situazione, creare un RedirectResponse non è appropriato.

Questo deve essere riscritta, ma inserisco la procedura prima su cui ho basato il mio servizio

$expires = time() + 2628000; 
$hash = hash_hmac(
    'sha256', 
    get_class($user).$user->getUsername().$expires.$user->getPassword(), 'secret in parameters.yml' 
); 
$value = base64_encode(implode(':', [get_class($user), base64_encode($user->getUsername()), $expires, $hash])); 
setcookie(
    'REMEMBERME', 
    $value, 
    $expires, 
    '/', 
    'host', 
    'ssl boolean', 
    true 
);