Symfony:为拒绝访问的安全投票者提供消息?

Symfony:为拒绝访问的安全投票者提供消息?,symfony,Symfony,我正在为一个API做安全性方面的工作。我在每个请求中检查的内容包括: 用户的IP地址是否已列入白名单以供访问 用户的帐户是否已过期 是否超出了用户当天的费率限制 似乎我应该使用一个安全投票者,可能每个投票者一个,当访问检查失败时返回VoterInterface::ACCESS\u DENIED 但是,我想在API响应中向用户提供一条消息,该消息提供一些指示,说明他们的请求被拒绝的原因。我不能对一个安全选民这样做 我当前的解决方法是侦听内核控制器事件,执行我的访问检查,然后在检查失败时抛出带有我的

我正在为一个API做安全性方面的工作。我在每个请求中检查的内容包括:

  • 用户的IP地址是否已列入白名单以供访问
  • 用户的帐户是否已过期
  • 是否超出了用户当天的费率限制
  • 似乎我应该使用一个安全投票者,可能每个投票者一个,当访问检查失败时返回
    VoterInterface::ACCESS\u DENIED

    但是,我想在API响应中向用户提供一条消息,该消息提供一些指示,说明他们的请求被拒绝的原因。我不能对一个安全选民这样做

    我当前的解决方法是侦听内核
    控制器
    事件,执行我的访问检查,然后在检查失败时抛出带有我的特定消息的显式
    AccessDeniedException


    这是处理这个缺点的好方法吗?也许在我忽略的安全投票者中有一种方法可以做到这一点?

    好的,我和你有同样的问题。对于这个问题,没有简单的方法,您必须使用以下行中的内容覆盖Symfony
    Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener的默认
    SecurityListener

    use Sensio\Bundle\FrameworkExtraBundle\EventListener\SecurityListener;
    
    class MySecurityListener extends SecurityListener 
    {
        public function onKernelController(FilterControllerEvent $event)
        {
            $request = $event->getRequest();
            if (!$configuration = $request->attributes->get('_acme_security')) {
                return;
            }
    
            // trick to simulate one security configuration (all in one class/method).
            $request->attributes->set('_security', new SecurityConfiguration($configuration));
    
            if (!$this->language->evaluate($configuration->getExpression(), $this->getVariables($request))) {
                throw new AccessDeniedException(sprintf($configuration->getMessage());
            }
    
            parent::onKernelController($event);
        }
    }
    
    除此之外,您还需要扩展
    Sensio\Bundle\FrameworkExtraBundle\Configuration\Security
    ,如下所示:

    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security as SensioSecurity;
    
    /**
     * @Annotation
     */
    class Security extends SensioSecurity
    {
        protected $message;
    
        public function getAliasName()
        {
            return 'acme_security';
        }
    
        public function getMessage()
        {
            return $this->message;
        }
    }
    
    这将允许您将
    消息
    属性添加到控制器注释中,如果存在AccessDenied异常,将使用该属性

    下面介绍如何在Yaml中配置安全侦听器:

      acme.security.listener:
        class: AppBundle\EventListener\SecurityListener
        parent: sensio_framework_extra.security.listener
        tags:
          - { name: kernel.event_subscriber }
    

    我知道这是一篇老文章,但我也遇到了同样的问题,找到了一个有效的解决方案

    PS:我正在使用symfony 3.4.4

    其思想是将
    RequestStack
    传递给投票者(构造函数注入),然后获取会话并在flashBag中添加一条消息,然后显示

    投票者构造函数:

    use Symfony\Component\HttpFoundation\RequestStack;
    
    class ContactVoter extends Voter {
    
    private $requestStack;
    
    function __construct(RequestStack $requestStack) {
        $this->requestStack= $requestStack;
    }
    
    如果您不使用autowire并希望将其作为参数传递到service.yml中,则可以使用
    参数:[@request\u stack]

    在投票者内部,决定权限的函数:

    if ( 'Your_Access_Denied_Condition') {
        $this->requestStack->getCurrentRequest()->getSession()->getFlashBag()->add('danger', 'Your message !');
        return false ;
    }
    
    显示消息的模板

    {% if app.request.hasPreviousSession %}
                {% for type, messages in app.session.flashbag.all() %}
                    {% for message in messages %}
                        <div class="alert alert-{{ type }}">
                            {{ message|trans({}, 'messages') }}
                        </div>
                    {% endfor %}
                {% endfor %}
            {% endif %}
    
    {%if-app.request.hasPreviousSession%}
    {%对于类型,app.session.flashbag.all()中的消息%}
    {消息%中的消息为%s}
    {{message | trans({},'messages')}
    {%endfor%}
    {%endfor%}
    {%endif%}
    

    如果您不想使用flashBag,您可以使用相同的逻辑抛出带有特定消息的自定义异常,通过侦听器捕获它并显示您想要的消息。

    这一功能适合您吗?可能是它的一种变体。我可以创建三个独立的投票者,然后修改我的内核控制器事件侦听器来执行这些投票者,这样我就不必在每个操作中都包含代码。