9

有人可以解释一下如何在控制器中手动创建记住我的 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

4 回答 4

14

这是我的做法。我没有使用 FOSUserBundle,我使用的是 Doctrine Entity User Provider,但它应该很容易适应您的需求。这是一个通用的解决方案:

// 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 optiontrue(就像我在上面的代码中所做的那样)或以某种方式将其放在 $_POST 参数中,否则方法isRememberMeRequestedAbstractRememberMeServices返回 false 并且不会存储 cookie。

不过,您非常接近正确的解决方案:)您做错的事情(在第三次尝试中)是您在这里更改了参数的顺序:

$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. 您应该将 a$securityKey作为第二个参数和$providerKey第三个参数传递,而不是像您错误地那样传递;)

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

于 2012-01-30T20:13:02.273 回答
10

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

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

哈希将在哪里:

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

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

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

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

但是,我不建议直接使用这种方式,而是尝试查看是否可以调用该TokenBasedRememberMeService::onLoginSuccess()方法,该方法为您设置此 cookie 以使代码更加健壮和可移植。

于 2012-01-30T18:48:43.140 回答
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。

于 2017-01-13T18:02:40.397 回答
0

当我尝试在通过令牌连接后使用Guard Authentication将 REMEMBERME cookie 设置为 User 时,我遇到了同样的问题。

在这种情况下,我没有 Response 对象可以使用 $response->headers->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
);
于 2017-03-30T10:48:41.190 回答