2012-04-10 7 views
8

Я хотел бы проверить изнутри контроллера, если это защищенная страница или нет. Как это сделать?Symfony2: как проверить, защищено ли действие?

Мой случай использования является следующее:

  • Пользователь может зарегистрироваться и войти
  • Если он входит в систему и пытается получить доступ к защищенному страницу, он будет перенаправлен на страницу «бета-версия» до в конце июня.
  • Если он пытается получить доступ к обычной странице (не защищен), он сможет получить к ней доступ без какого-либо перенаправления.

Благодарим за помощь!

Аурел

+0

Я не уверен, чего вы пытаетесь достичь. Когда пользователь набирает URL (или перенаправляется на) действие **, действие защищено или не защищено **. Очевидно, что контроллеры не могут иметь несколько действий (защищенных и не защищенных) с тем же именем из-за имени метода. Однако вы можете спросить SecureContext, если посетитель имеет соответствующую роль, а затем сделает что-то с запросом (например, переадресация, перенаправление) –

ответ

15

Когда Symfony2 обрабатывает запрос, он соответствует шаблону url с каждым брандмауэром, определенным в app/config/security.yml. Когда шаблон url совпадает с шаблоном брандмауэра Symfony2 создает некоторые объекты-слушатели и вызывает метод handle этих объектов. Если какой-либо прослушиватель возвращает объект Response, тогда цикл прерывается, а Symfony2 выводит ответ. Часть аутентификации выполняется в прослушивателях аутентификации. Они создаются из конфигурации, определенной в согласованном брандмауэре, например, form_login, http_basic и т. Д. Если пользователь не аутентифицирован, тогда аутентифицированные слушатели создают объект RedirectResponse для перенаправления пользователя на страницу входа. Для вашего случая вы можете обмануть, создав пользовательский прослушиватель аутентификации и добавив его в защищенный брандмауэр страниц. Пример реализации будет следующее,

Создать Token класс,

namespace Your\Namespace; 

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

class MyToken extends AbstractToken 
{ 
    public function __construct(array $roles = array()) 
    { 
     parent::__construct($roles); 
    } 

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

Создать класс, который реализует AuthenticationProviderInterface. Для прослушивателя form_login он аутентифицируется с данными UserProvider. В этом случае он ничего не сделает.

namespace Your\Namespace; 

use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; 
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface; 
use Acme\BaseBundle\Firewall\MyToken; 

class MyAuthProvider implements AuthenticationProviderInterface 
{ 

    public function authenticate(TokenInterface $token) 
    { 
     if (!$this->supports($token)) { 
      return null; 
     } 

     throw new \Exception('you should not get here'); 
    } 

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

Создать класс точки входа. Слушатель создаст RedirectResponse из этого класса.

namespace Your\Namespace; 

use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\HttpFoundation\Response; 
use Symfony\Component\Security\Core\Exception\AuthenticationException; 
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; 
use Symfony\Component\Security\Http\HttpUtils; 


class MyAuthenticationEntryPoint implements AuthenticationEntryPointInterface 
{ 
    private $httpUtils; 
    private $redirectPath; 

    public function __construct(HttpUtils $httpUtils, $redirectPath) 
    { 
     $this->httpUtils = $httpUtils; 
     $this->redirectPath = $redirectPath; 
    } 

    /** 
    * {@inheritdoc} 
    */ 
    public function start(Request $request, AuthenticationException $authException = null) 
    { 
     //redirect action goes here 
     return $this->httpUtils->createRedirectResponse($request, $this->redirectPath); 
    } 

Создать класс слушателя. Здесь вы реализуете свою логику перенаправления.

namespace Your\Namespace; 

use Symfony\Component\Security\Http\Firewall\ListenerInterface; 
use Symfony\Component\HttpKernel\Event\GetResponseEvent; 
use Symfony\Component\Security\Core\SecurityContextInterface; 
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface; 

class MyAuthenticationListener implements ListenerInterface 
{ 
    private $securityContext; 
    private $authenticationEntryPoint; 


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

    public function handle(GetResponseEvent $event) 
    { 
     $token = $this->securityContext->getToken(); 
     $request = $event->getRequest(); 
     if($token === null){ 
      return; 
     } 

     //add your logic 
     $redirect = // boolean value based on your logic 

     if($token->isAuthenticated() && $redirect){ 

      $response = $this->authenticationEntryPoint->start($request); 
      $event->setResponse($response); 
      return; 
     } 
    } 

} 

Создайте услуги.

<?xml version="1.0" ?> 
<container xmlns="http://symfony.com/schema/dic/services" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> 

    <services> 

     <service id="my_firewall.security.authentication.listener" 
       class="Your\Namespace\MyAuthenticationListener" 
       parent="security.authentication.listener.abstract" 
       abstract="true"> 
      <argument type="service" id="security.context" /> 
      <argument /> <!-- Entry Point --> 
     </service> 

     <service id="my_firewall.entry_point" class="Your\Namespace\MyAuthenticationEntryPoint" public="false" ></service> 

     <service id="my_firewall.auth_provider" class="Your\Namespace\MyAuthProvider" public="false"></service> 
    </services> 

</container> 

Зарегистрировать слушателя. Создайте папку с именем Security/Factory в папке DependencyInjection. Затем создайте фабричный класс.

namespace Your\Bundle\DependencyInjection\Security\Factory; 

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

class MyFirewallFactory implements SecurityFactoryInterface 
{ 

    public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) 
    { 
     $provider = 'my_firewall.auth_provider.'.$id; 
     $container->setDefinition($provider, new DefinitionDecorator('my_firewall.auth_provider')); 

     // entry point 
     $entryPointId = $this->createEntryPoint($container, $id, $config, $defaultEntryPoint); 

     // listener 
     $listenerId = 'my_firewall.security.authentication.listener'.$id; 
     $listener = $container->setDefinition($listenerId, new DefinitionDecorator('my_firewall.security.authentication.listener')); 
     $listener->replaceArgument(1, new Reference($entryPointId)); 
     return array($provider, $listenerId, $entryPointId); 
    } 

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

    public function getKey() 
    { 
     return 'my_firewall'; //the listener name 
    } 

    protected function getListenerId() 
    { 
     return 'my_firewall.security.authentication.listener'; 
    } 

    public function addConfiguration(NodeDefinition $node) 
    { 
     $node 
      ->children() 
       ->scalarNode('redirect_path')->end() 
      ->end() 
      ; 
    } 

    protected function createEntryPoint($container, $id, $config, $defaultEntryPointId) 
    { 
     $entryPointId = 'my_firewall.entry_point'.$id; 
     $container 
      ->setDefinition($entryPointId, new DefinitionDecorator('my_firewall.entry_point')) 
      ->addArgument(new Reference('security.http_utils')) 
      ->addArgument($config['redirect_path']) 
      ; 
     return $entryPointId; 
    } 

} 

Тогда в вашем NamespaceBundle.php из папки расслоения добавьте следующий код.

public function build(ContainerBuilder $builder){ 
    parent::build($builder); 
    $extension = $builder->getExtension('security'); 
    $extension->addSecurityListenerFactory(new Security\Factory\MyFirewallFactory()); 
} 

Аутентификационный слушатель создан, пю :). Теперь в вашем app/config/security.yml выполните следующие действия.

api_area: 
    pattern: ^/secured/ 
    provider: fos_userbundle 
    form_login: 
    check_path: /login_check 
    login_path: /login 
    csrf_provider: form.csrf_provider 
    my_firewall: 
    redirect_path: /beta 
    logout: true 
    anonymous: true 
+1

Фу, хороший ответ! +1 – halfer

0

Я не знаю, если это правильный метод Но вы можете попробовать следующее

/vendor/symfony/src/Symfony/Component/HttpKernel/HttpKernel.php имеет метод handleRaw, который преобразует запрос в ответ. Объект запроса доступен оттуда. Вы можете проверить, запросил ли клиент доступ к защищенной странице. Если да, то вы можете вручную установить контроллер, как

$request->attributes->set('_controller','your\Bundle\SecureBundle\Controller\SecureController::secureAction'); 

Другим решением было бы установить сессию, если пользователь пытается получить доступ к защищенной странице и проверить то же самое внутри контроллер

Опять же, это может не является правильным методом, но это возможно.

Смежные вопросы