创建一个symfony2手动记住我的cookie(FOSUserBundle)

时间:2012-01-22 18:27:34

标签: symfony remember-me fosuserbundle

有人可以解释一下如何在控制器中手动创建记住我的cookie吗?

我希望用户在按下“注册”后保持登录状态 按钮,之后无需使用凭据登录。

我试图手动创建一个cookie,但我猜的是cookie 价值不正确,因此“记住我”的功能 不起作用。 设置具有正确名称的cookie。我检查过了。

记住我的功能在使用法线时按预期工作 使用用户凭据登录过程。

security.yml security.yml记得我

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

这就是我现在所拥有的,即使设置了cookie,它也不起作用。

$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;

更新

我也尝试过使用 PersistentTokenBasedRememberMeServices类具有反射但它不起作用。一个cookie被设置但它不起作用

$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);

我正在使用Symfony v2.0.5和FOSUserBundle 1.0

更新2:

我尝试了第三种方式。与上述相同但没有反思:

$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);

4 个答案:

答案 0 :(得分:13)

我是这样做的。我没有使用FOSUserBundle而且我正在使用Doctrine Entity用户提供程序,但根据您的需求进行调整应该是微不足道的。这是一个通用的解决方案:

// 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;

请记住,您必须将always_remember_me option设置为true(就像我在上面的代码中所做的那样)或者以某种方式将它放在$ _POST参数中,否则isRememberMeRequested的{​​{1}}将返回false并且不会存储cookie。

你非常接近正确的解决方案:)你做错了(在第3次尝试中)是你在这里改变了参数的顺序:

AbstractRememberMeServices

查看$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')); 中的__construct()。你应该传递一个AbstractRememberMeServices.php作为第二个参数而$securityKey作为第三个参数,而不是像你误做的那样传递;)

我还不知道的是,如何直接在控制器中从security.yml获取参数,而不是复制它。通过使用$providerKey,我可以在config.yml中获取存储在$this->container->getParameter()键下的参数,但不能在配置树中获得更高的参数。有什么想法吗?

答案 1 :(得分:10)

如果您直接设置记忆cookie,则必须使用以下格式:

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

哈希将是:

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

密钥是您在remember_me部分中输入安全性(.xml / .yml)的密钥。

这取自 Symfony / Component / Security / Http / RememberMe / TokenBasedRememberMeService.php 文件中的processAutoLoginCookie()方法。

这都是由同一个类中的generateCookieValue()方法完成的。

但是,我不建议直接以这种方式使用它,但是试着看看你是否可以调用TokenBasedRememberMeService::onLoginSuccess()方法,它为你设置这个cookie以使代码更健壮和可移植。

答案 2 :(得分:0)

对我来说,最简单的解决方案是扩展BaseTokenBasedRememberMeServices并让它处理

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;
    }
}

并在控制器中;

$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());

然后将cookie设置为$ remember_me_cookie

我希望它能与你合作2。

答案 3 :(得分:0)

当我尝试使用Guard Authentication按令牌连接后将REMEMBERME Cookie设置为用户时,我遇到了同样的问题。

在这种情况下,我没有Response对象能够使用$ response-&gt; headers-&gt; setCookie()并且需要使用setcookie()。 在这种情况下,创建RedirectResponse是不合适的。

这需要重构,但我发布了基于我的服务的原始程序

$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
);