今天,我开始将我的应用程序从symfony 3升级到4(以及相关的库),但我不明白为什么我无法使某些路由正常工作(我遇到了401错误,但它们应该是公共路由)因此在那里没有进行安全检查),然后我最终发现了这个问题:@Security annotation on controller class being overridden by action method
最近对该问题的评论说,在先前版本的symfony框架额外捆绑中,如果将安全注释同时放在一个类和该类中的方法上,则该方法注释将覆盖该类注释,现在它们堆叠代替。
这也可以在版本4.0的SensioFramework更改日志https://github.com/sensiolabs/SensioFrameworkExtraBundle/blob/master/CHANGELOG.md上看到(虽然不是很清楚,因为您已经在类和方法上都添加了@Security注释)
允许使用多个@Security批注(类和方法)
对我来说,这是一个很大的变化,因为我的应用程序中的许多路由都依赖于该行为(类似于Symfony 1,您可以在其中设置默认的安全行为,然后为每个操作指定一个更具体的行为)>
/**
* @Route("my-route")
* @Security("is_granted('IS_AUTHENTICATED_FULLY')")
*/
class MyController extends Controller {
/**
* In Symfony 3.x this would've removed security checks for the route,
* now it checks both the class and the method Security expressions
* @Security(true)
*/
public function myAction(Request $request) {
}
}
除了“不升级到symfony 4”或“重新组织代码”(这是我的“计划B”)以外,还有其他方法可以使此行为恢复吗?诸如配置选项之类的东西。 我似乎找不到任何有关此的信息
答案 0 :(得分:1)
我忘记了这个问题,但是确实通过制作自己的注释和EventListener来解决了这个问题。 免责声明:
1)我的代码使用Dependency Injection捆绑软件通过注释注入和声明服务
2)我按原样共享代码,没有任何保证,它也对您有用,但我希望您能从中得到要点
我创建了2个注释(@IsGrantedDefault和@SecurityDefault),它们的工作方式与@IsGranted和@Security(它们实际上是对原始注释的扩展)完全一样,只是它们只能应用于类,然后我创建了2个事件侦听器,每个事件侦听器一个注解。事件侦听器还扩展了原始事件侦听器,但它们只是检查某个方法是否已具有Security或IsGranted批注,在这种情况下,它们将不执行任何操作。
IsGrantedDefault.php
<?php
/*
* @author valepu
*/
namespace App\Project\AppBundle\Annotation;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
/**
* @Annotation
* @Target("CLASS")
*/
class IsGrantedDefault extends IsGranted {
public function getAliasName() {
return 'is_granted_default';
}
public function allowArray() {
return false;
}
}
SecurityDefault.php
<?php
/*
* @author valepu
*/
namespace App\Project\AppBundle\Annotation;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
/**
* @Annotation
* @Target("CLASS")
*/
class SecurityDefault extends Security {
public function getAliasName() {
return 'security_default';
}
public function allowArray() {
return false;
}
}
DefaultListenerTrait.php(值:: DEFAULT_LISTENER_PREFIX只是带有下划线“ _”的字符串)
<?php
/*
* @author valepu
*/
namespace App\Project\AppBundle\Event\Traits;
use App\Project\AppBundle\Utils\Values;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
Trait DefaultListenerTrait {
/**
* @var string
*/
private $defaultAttribute;
/**
* @var string
*/
private $otherAttributes = [];
/**
* @var string
*/
private $attribute;
/**
* Sets the class attributes
* @param [type] $defaultAnnotation
* @param string|null $modifyAttr
* @return void
*/
protected function setAttributes($defaultAnnotation, ?string $modifyAttr) {
//Get the attirbutes names
$this->attribute = $modifyAttr;
$this->defaultAttribute = Values::DEFAULT_LISTENER_PREFIX . $defaultAnnotation->getAliasName();
$annotations = [new IsGranted([]), new Security([])];
foreach($annotations as $annotation) {
$this->otherAttributes[] = Values::DEFAULT_LISTENER_PREFIX . $annotation->getAliasName();
}
}
/**
* Checks wheter or not the request needs to be handled by the annotation. If it does adds the correct attribute to the request
* @param \Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent $event
* @return boolean
*/
protected function updateDefaultListener(FilterControllerArgumentsEvent $event) {
$request = $event->getRequest();
$default = $request->attributes->get($this->defaultAttribute);
//If there's already an "IsGranted" annotation or there's no "IsGrantedDefault" annotation
if (!$default) {
return false;
}
foreach($this->otherAttributes as $attr) {
if ($request->attributes->get($attr) || !$default) {
return false;
}
}
//We set IsGranted from the default and then call the parent eventListener so that it can handle the security
$request->attributes->set($this->attribute, [$default]);
return true;
}
/**
* Calls the event listener for the class if the request is handled by the class
* @param \Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent $event
* @return void
*/
protected function callEventListener(FilterControllerArgumentsEvent $event) {
if($this->updateDefaultListener($event)) {
parent::onKernelControllerArguments($event);
}
}
}
IsGrantedDefaultListener.php
<?php
/*
* @author valepu
*/
namespace App\Project\AppBundle\Event;
use App\Project\AppBundle\Annotation\IsGrantedDefault;
use App\Project\AppBundle\Event\Traits\DefaultListenerTrait;
use App\Project\AppBundle\Utils\Values;
use RS\DiExtraBundle\Annotation as DI;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\IsGrantedListener;
use Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
/**
* @DI\Service(autowire = true)
* @DI\Tag("kernel.event_subscriber")
*/
class IsGrantedDefaultListener extends IsGrantedListener {
use DefaultListenerTrait;
/**
* @param \Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter $argumentNameConverter
* @param \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface $authChecker
* @DI\InjectParams({
* "argumentNameConverter" = @DI\Inject("framework_extra_bundle.argument_name_convertor"),
* "authChecker" = @DI\Inject("security.authorization_checker")
* })
*/
public function __construct(ArgumentNameConverter $argumentNameConverter, AuthorizationCheckerInterface $authChecker = null) {
parent::__construct($argumentNameConverter, $authChecker);
$modifyAttr = new IsGranted([]);
$this->setAttributes(new IsGrantedDefault([]), Values::DEFAULT_LISTENER_PREFIX . $modifyAttr->getAliasName());
}
/**
* @param \Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent $event
* @return void
*/
public function onKernelControllerArguments(FilterControllerArgumentsEvent $event) {
$this->callEventListener($event);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments'];
}
}
SecurityDefaultListener.php
<?php
/*
* @author valepu
*/
namespace App\Project\AppBundle\Event;
use App\Project\AppBundle\Annotation\SecurityDefault;
use App\Project\AppBundle\Event\Traits\DefaultListenerTrait;
use App\Project\AppBundle\Utils\Values;
use Psr\Log\LoggerInterface;
use RS\DiExtraBundle\Annotation as DI;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener;
use Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter;
use Sensio\Bundle\FrameworkExtraBundle\Security\ExpressionLanguage;
use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
/**
* @DI\Service(autowire = true)
* @DI\Tag("kernel.event_subscriber")
*/
class SecurityDefaultListener extends SecurityListener {
use DefaultListenerTrait;
/**
* @param \Sensio\Bundle\FrameworkExtraBundle\Request\ArgumentNameConverter $argumentNameConverter
* @param \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface $authChecker
* @DI\InjectParams({
* "argumentNameConverter" = @DI\Inject("framework_extra_bundle.argument_name_convertor"),
* "language" = @DI\Inject("sensio_framework_extra.security.expression_language.default"),
* "trustResolver" = @DI\Inject("security.authentication.trust_resolver"),
* "roleHierarchy" = @DI\Inject("security.role_hierarchy"),
* "tokenStorage" = @DI\Inject("security.token_storage"),
* "authChecker" = @DI\Inject("security.authorization_checker"),
* "logger" = @DI\Inject("logger")
* })
*
*/
public function __construct(ArgumentNameConverter $argumentNameConverter, ExpressionLanguage $language = null, AuthenticationTrustResolverInterface $trustResolver = null, RoleHierarchyInterface $roleHierarchy = null, TokenStorageInterface $tokenStorage = null, AuthorizationCheckerInterface $authChecker = null, LoggerInterface $logger = null) {
parent::__construct($argumentNameConverter, $language, $trustResolver, $roleHierarchy, $tokenStorage, $authChecker, $logger);
$modifyAttr = new Security([]);
$this->setAttributes(new SecurityDefault([]), Values::DEFAULT_LISTENER_PREFIX . $modifyAttr->getAliasName());
}
public function onKernelControllerArguments(FilterControllerArgumentsEvent $event) {
$this->callEventListener($event);
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [KernelEvents::CONTROLLER_ARGUMENTS => 'onKernelControllerArguments'];
}
}
答案 1 :(得分:-1)
您可以删除类批注并在所有方法上声明它们