Symfony 2 - 重定向后丢失身份验证令牌

时间:2013-09-29 15:46:18

标签: php authentication symfony

我有symfony2的问题(我已经尝试过与Symfony 2.0和Symfony 2.3一样只是为了查看它是否是一个Symfony错误),我在下一页加载/重定向后丢失了安全令牌。

我已经为Symfony 2.3创建了自定义身份验证器,以使用此处指定的第三方服务进行身份验证:http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html

应用程序使用外部服务进行身份验证并在回调URL'/ success'中设置令牌,我可以从调试栏中看到用户已通过身份验证,但是当我转到'/'(位于同一防火墙下)时我收到“在SecurityContext中找不到令牌”。错误和用户不再经过身份验证。

以下是文件:

security.yml

security:
    encoders:
        Symfony\Component\Security\Core\User\User: plaintext

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        users:
            entity: { class: AcmeStoreBundle:User, property: email }

    firewalls:
        login:
            pattern:  ^/login$
            security: false
        noa:
            pattern:  ^/
            provider: users
            noa: true
            logout:
                path:   /logout
                target: /login

    access_control:
        - { path: ^/success, roles: IS_AUTHENTICATED_ANONYMOUSLY }

NoaUserToken.php

<?php
namespace Acme\StoreBundle\Security\Authentication\Token;

use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;

class NoaUserToken extends AbstractToken
{
    public $expires;
    public $mobile;
    public $email;

    public function __construct(array $roles = array())
    {
        parent::__construct($roles);

        parent::setAuthenticated(true);
    }

    public function getCredentials()
    {
        return '';
    }
}

NoaProvider.php

<?php
namespace Acme\StoreBundle\Security\Authentication\Provider;

use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Acme\StoreBundle\Security\Authentication\Token\NoaUserToken;

class NoaProvider implements AuthenticationProviderInterface
{
    private $userProvider;
    private $cacheDir;

    public function __construct(UserProviderInterface $userProvider, $cacheDir)
    {
        $this->userProvider = $userProvider;
        $this->cacheDir     = $cacheDir;
    }

    public function authenticate(TokenInterface $token)
    {
        $userEmail = $token->getUser();

        $user = $this->userProvider->loadUserByUsername($userEmail);

        if ($user && $this->validateToken($token->expires) && !$user->getHidden()) {
            $authenticatedToken = new NoaUserToken($user->getRoles());
            $authenticatedToken->expires = $token->expires;
            $authenticatedToken->mobile = $token->mobile;
            $authenticatedToken->email = $token->email;
            $authenticatedToken->setUser($user);
            $authenticatedToken->setAuthenticated(true);

            return $authenticatedToken;
        }

        throw new AuthenticationException('The NOA authentication failed.');
    }

    protected function validateToken($expires)
    {
        // Check if the token has expired.
        if (strtotime($expires) <= time()) {
            return false;
        }
    }

    public function supports(TokenInterface $token)
    {
        return $token instanceof NoaUserToken;
    }
}

NoaListener.php

<?php
namespace Acme\StoreBundle\Security\Firewall;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Acme\StoreBundle\Security\Authentication\Token\NoaUserToken;

class NoaListener implements ListenerInterface
{
    protected $securityContext;
    protected $authenticationManager;

    public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager)
    {
        $this->securityContext = $securityContext;
        $this->authenticationManager = $authenticationManager;
    }

    public function handle(GetResponseEvent $event)
    {
        $request = $event->getRequest();

        if (! preg_match('/^\/app_dev.php\/success/', $request->getRequestUri())) {
            return;
        }

        if( $this->securityContext->getToken() ){
            return;
        }

        try {
            \NOA_Sso_Web::getInstance()->createSession();
        } 
        catch (Exception $e) {
            // Handle error situation here
        }

        if (isset($_SESSION['userInfo'])) {
            $token = new NoaUserToken();
            $token->setUser($_SESSION['userInfo']['email']);

            $token->mobile   = $_SESSION['userInfo']['mobileVerified'] ? $_SESSION['userInfo']['mobile'] : null;
            $token->email    = $_SESSION['userInfo']['emailVerified'] ? $_SESSION['userInfo']['email'] : null;
            $token->expires  = $_SESSION['tokenInfo']['expires'];

            try {
                $authToken = $this->authenticationManager->authenticate($token);
                $this->securityContext->setToken($authToken);
                return;
            } catch (AuthenticationException $failed) {
                // Do nothing and go for the default 403
            }
        }

        $this->securityContext->setToken(null);

        // Deny authentication with a '403 Forbidden' HTTP response
        $response = new Response();
        $response->setStatusCode(403);
        $event->setResponse($response);
    }
}

NoaFactory.php

<?php
namespace Acme\StoreBundle\DependencyInjection\Security\Factory;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;

class NoaFactory implements SecurityFactoryInterface
{
    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
    {
        $providerId = 'security.authentication.provider.noa.'.$id;
        $container
            ->setDefinition($providerId, new DefinitionDecorator('noa.security.authentication.provider'))
            ->replaceArgument(0, new Reference($userProvider))
        ;

        $listenerId = 'security.authentication.listener.noa.'.$id;
        $listener = $container->setDefinition($listenerId, new DefinitionDecorator('noa.security.authentication.listener'));

        return array($providerId, $listenerId, $defaultEntryPoint);
    }

    public function getPosition()
    {
        return 'pre_auth';
    }

    public function getKey()
    {
        return 'noa';
    }

    public function addConfiguration(NodeDefinition $node)
    {
    }
}

DefaultController.php

<?php

namespace Acme\StoreBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    public function indexAction()
    {
        $token = $this->container->get('security.context')->getToken();

        // Not reached
        print '<pre>';
        print_r($token->getUser());
        print '</pre>';
        return $this->render('AcmeStoreBundle:Default:index.html.twig', array('name' => $token->getUser()->gerUsername()));
    }
    public function loginAction()
    {
        return $this->render('AcmeStoreBundle:Default:login.html.twig', array());
    }
    public function successAction()
    {
        $token = $this->container->get('security.context')->getToken();
        $this->container->get('event_dispatcher')->dispatch(
            SecurityEvents::INTERACTIVE_LOGIN,
            new InteractiveLoginEvent($this->container->get('request'), $token)
        );

        // This prints the user object
        print '<pre>';
        print_r($token->getUser());
        print '</pre>';
        return new Response('<script>//window.top.refreshPage();</script>');
    }
}

我已经检查了stackoverflow中的所有类似问题并花了大约一个星期来解决这个问题,非常感谢任何帮助。

1 个答案:

答案 0 :(得分:0)

您应该在请求对象上使用会话接口,而不是在NoaListener中使用$_SESSION。 Symfony执行自己的会话管理,可能会忽略或覆盖您的会话(例如,成功登录后迁移会话以防止会话固定攻击是常见的。)

使用您现有的$request = $event->getRequest(),然后使用$request->getSesssion()->get('userInfo')等。