7

I'm trying to implement single sign on access to a website using Symfony2.

The authentication itself seems to work fine, but only for the initial page. On the next page that is loaded the user is not logged in anymore.

Relevant code:

$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$event = new InteractiveLoginEvent($request, $token);

$this->get("event_dispatcher")->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $event);
$this->get("security.context")->setToken($token);

return $this->redirect($this->generateUrl('sonata_user_profile_show'));

First page (without the redirect):

Initial page - logged in

Second page:

Second page - Not logged in anymore

4

1 回答 1

6

自定义登录只需要以下代码。

$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get("security.context")->setToken($token);

return $this->redirect($this->generateUrl('sonata_user_profile_show'));

这样做是在安全上下文中设置 UsernamePasswordToken。该令牌(以及用户)将被序列化并放入会话中。在下一页上,令牌将从会话中反序列化,并且同样反序列化的用户将被刷新。

FOSUserBundle 中的用户提供者使用未序列化用户的 id 执行此刷新。

此外,Doctrine2 在某些情况下使用代理类作为实体类而不是原始实体类。该代理类通过复杂的延迟加载复杂实现覆盖实体的“getId()”函数。

这一起可能导致这样一个事实,当您将 Doctrine2 代理对象放入 UserPasswordToken 时,序列化和非序列化代理对象的“getId()”将不会返回原始 ID。发生这种情况时,用户提供者无法刷新用户,并且令牌将变为无效。

解决此问题的方法是创建一个自定义用户提供程序,该用户提供程序通过使用用户名(或其他唯一属性)刷新来覆盖“refreshUser()”。

//...
class UserProvider extends FOSUserProvider
{
    /**
     * {@inheritDoc}
     */
    public function refreshUser(SecurityUserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(sprintf('Expected an instance of User, but got "%s".', get_class($user)));
        }

        if (null === $reloadedUser = $this->userManager->findUserBy(array('username' => $user->getUsername()))) {
            throw new UsernameNotFoundException(sprintf('User with username "%s" could not be reloaded.', $user->getUsername()));
        }

        return $reloadedUser;
    }
}
于 2013-01-15T14:26:31.210 回答