2013-09-29 92 views
0

我遇到了symfony2問題(我嘗試過與Symfony 2.0相同的問題& Symfony 2.3只是爲了查看它是否是Symfony的bug),我丟失了下一頁加載/認證後重定向。Symfony 2 - 身份驗證令牌在重定向後丟失

我已創建自定義的驗證器Symfony的2.3與第三方服務來驗證所指定的位置:http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html

應用與外部服務認證和設置令牌回調URL「/成功」,我可以從調試欄中看到用戶已通過身份驗證,但是當我進入'/'(位於同一防火牆下)時,我得到了「在SecurityContext中找不到Token」。錯誤和用戶不再通過身份驗證。

以下是文件:

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

我已檢查所有s stackoverflow中的類似問題,並花了大約一週的時間來解決這個問題,任何幫助非常感謝。

回答

0

而不是在NoaListener中使用$_SESSION,您應該在請求對象上使用會話接口。 Symfony執行自己的會話管理,可能會忽略或覆蓋會話(例如,在成功登錄後遷移會話以防止會話修復攻擊是常見的)。

使用$request = $event->getRequest()因爲你已經有了,然後$request->getSesssion()->get('userInfo')

+0

非常感謝你對inanimatt注意到,實際上第三方庫復位會話,我不得不重新改寫會議第三方庫來處理解決這個問題。 – dennismiller