【问题标题】:Create a symfony2 remember me cookie manually (FOSUserBundle)手动创建 symfony2 记住我的 cookie (FOSUserBundle)
【发布时间】:2012-01-22 18:27:33
【问题描述】:

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

【问题讨论】:

    标签: symfony remember-me fosuserbundle


    【解决方案1】:

    这就是我的做法。我没有使用 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 option 设置为 true(就像我在上面的代码中所做的那样)或以某种方式将其放在 $_POST 参数中,否则 AbstractRememberMeServices 的方法 isRememberMeRequested 将返回 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'));
    

    AbstractRememberMeServices.php 中查看__construct()。您应该将$securityKey 作为第二个参数传递,将$providerKey 作为第三个参数传递,而不是像您错误地那样传递;)

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

    【讨论】:

    • 为了在 security.yml 中获取 cookie 参数并避免重复,您应该将它们放在 parameters.yml 中,并在 security.yml 中使用 %your_parameter_name% 调用它们。它们现在可用于安全性和参数中
    • +1 为此使用 Symfony2 安全类。请记住,remember-me cookie 的默认 cookie 名称是 REMEMBERME。只有当您使用与防火墙相同的 cookie 名称时,您针对手动设置的 cookie 进行身份验证才会被安全侦听器识别。
    • 传递给 Symfony\Bridge\Doctrine\Security\User\EntityUserProvider::__construct() 的参数 1 必须实现接口 Doctrine\Common\Persistence\ManagerRegistry,我知道了 Doctrine\ORM\EntityManager 的实例D:
    • 通过在没有 entitymanager 的情况下仅发送学说来修复。所以基本上只有 $this->getDoctrine(),谢谢 :) 现在我需要确保它有效。
    【解决方案2】:

    如果你是直接设置rememberme 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 以使代码更加健壮和可移植。

    【讨论】:

      【解决方案3】:

      对我来说,最简单的解决方案是扩展 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.

      【讨论】:

        【解决方案4】:

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

        在这种情况下,我没有 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
        );
        

        【讨论】:

          猜你喜欢
          • 2013-12-05
          • 2021-04-05
          • 2012-01-28
          • 2014-09-19
          • 1970-01-01
          • 1970-01-01
          • 2023-03-11
          • 2011-03-22
          • 1970-01-01
          相关资源
          最近更新 更多