我写了一个返回JSON的API。某些路由由Controller Action上的@Security注释保护。
如果is_granted()方法失败,我会捕获抛出的异常并输出一些带有403 http状态代码的错误json。
这有效,但仅在用户登录但权限不足时才有效。如果未登录,则会将用户重定向到登录页面(在ajax调用中完全没用)。
我该怎么做才能防止重定向?
我试图将以下行添加到security.yml access_control部分,但没有效果:
access_control:
- { path: ^/api, role: IS_AUTHENTICATED_ANONYMOUSLY }
我为Symfony 4写了一些非常相似的东西。
但是在我的代码中,不需要检查请求URI,因为只检查了主请求。此外,代码更清洁。来自安全套装的AccessDeniedException
被来自Symfony本身的AccessDeniedHttpException
所取代。这导致了一个真正的403 Exception页面,而不会丢失Debug的可能性。
// PHP class: App\EventListener\RestSecurity403ExceptionListener
namespace App\EventListener;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class RestSecurity403ExceptionListener
{
public function onKernelException(GetResponseForExceptionEvent $event)
{
// only check master request
if (!$event->isMasterRequest())
return;
// get variables
$exception = $event->getException();
$request = $event->getRequest();
// replace Security Bundle 403 with Symfony 403
if($exception instanceof AccessDeniedException)
throw new AccessDeniedHttpException("Symfony 403 error thrown instead of 403 error of the security bundle");
}
}
并在services.yaml
中添加Exception Listener:
# services.yaml
services:
my.RestSecurity403ExceptionListener:
class: App\EventListener\RestSecurity403ExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception, priority: 256 }
而已。
好的,经过几个小时的调试后,我发现这个行为是在安全组件的异常监听器(Symfony \ Component \ Security \ Http \ Firewall \ ExceptionListener)中硬编码的。
所以我必须使用onKernelException方法编写自己的ExceptionListener:
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
do {
if ($exception instanceof AccessDeniedException) {
if(substr($event->getRequest()->server->get('PATH_INFO'), 0, 4) == '/api') {
$event->setException(new AjaxAccessDeniedException());
}
}
} while (null !== $exception = $exception->getPrevious());
}
检查路径是否以/ api开头,并抛出我自己的AjaxAccessDeniedException。此异常与AccessDeniedException具有相同的代码,但不继承它(因为否则它将再次被安全组件ExceptionListener捕获)。这个我可以在异常控制器中捕获,因为它不会被其他地方捕获。
最后一步是将我的ExceptionListener注册为服务,但优先级高于默认值。
my.exception_listener:
class: Acme\MyBundle\EventListener\ExceptionListener
arguments: [@security.context, @security.authentication.trust_resolver]
tags:
- { name: kernel.event_listener, event: kernel.exception, priority: 256 }