我有一个用户对象,其中包含已启用的属性'。我希望每个操作在继续之前首先检查用户是否已启用。
现在我已经使用每个其他控制器扩展的控制器解决了这个问题,但是使用setContainer
函数来捕获每个控制器操作都感觉非常糟糕。
class BaseController extends Controller{
public function setContainer(ContainerInterface $container = null)
{
$this->container = $container;
$user = $this->getUser();
// Redirect disabled users to a info page
if (!$user->isEnabled() && !$this instanceof InfoController) {
return $this->redirectToRoute('path_to_info');
}
}
我尝试使用之前的过滤器(http://symfony.com/doc/current/event_dispatcher/before_after_filters.html)来构建它,但无法获取User对象..有哪些提示?
编辑:
这是我的解决方案:
namespace AppBundle\Security;
use AppBundle\Controller\AccessDeniedController;
use AppBundle\Controller\ConfirmController;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
class UserEnabledListener
{
private $tokenStorage;
private $router;
public function __construct(TokenStorage $tokenStorage, Router $router)
{
$this->tokenStorage = $tokenStorage;
$this->router = $router;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
/*
* $controller passed can be either a class or a Closure.
* This is not usual in Symfony but it may happen.
* If it is a class, it comes in array format
*/
if (!is_array($controller)) {
return;
}
$controller = $controller[0];
// Skip enabled check when:
// - we are already are the AccessDenied controller, or
// - user confirms e-mail and becomes enabled again, or
// - Twig throws error in template
if ($controller instanceof AccessDeniedController ||
$controller instanceof ConfirmController ||
$controller instanceof ExceptionController) {
return;
}
$user = $this->tokenStorage->getToken()->getUser();
// Show info page when user is disabled
if (!$user->isEnabled()) {
$redirectUrl = $this->router->generate('warning');
$event->setController(function() use ($redirectUrl) {
return new RedirectResponse($redirectUrl);
});
}
}
}
编辑2: 好的,所以事实证明手动检查每个控制器真的很糟糕,因为你会错过来自第三方依赖的控制器。我将使用安全注释,并在自定义的异常控制器或模板等中执行进一步的自定义逻辑。
答案 0 :(得分:4)
您可以使用event listener收听任何新请求。
您需要注入用户然后进行验证:
<service id="my_request_listener" class="Namespace\MyListener">
<tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" />
<argument type="service" id="security.token_storage" />
</service>
编辑:这是一个提供示例的代码段
class MyRequestListener {
private $tokenStorage;
public function __construct(TokenStorage $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function onKernelRequest(GetResponseEvent $event)
{
if (!$event->getRequest()->isMasterRequest()) {
// don't do anything if it's not the master request
return;
}
if ($this->tokenStorage->getToken()) {
$user = $this->tokenStorage->getToken()->getUser();
//do your verification here
}
}
答案 1 :(得分:3)
在你的情况下,我会使用@Security
注释,如果你使用表达式语言,它可以非常灵活。
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
/**
* @Security("user.isEnabled()")
*/
class EventController extends Controller
{
// ...
}
最后,每个控制器文件中只有1行,并且它具有非常易读的优点(项目新开发人员可以立即知道发生了什么,而无需去检查BaseController的内容或过滤器之前的任何潜力...)
有关此here的更多文档。
答案 2 :(得分:-2)
您也可以在getuser()
中覆盖BaseController
功能。
/**
* Get a user from the Security Token Storage.
*
* @return mixed
*
* @throws \LogicException If SecurityBundle is not available
*
* @see TokenInterface::getUser()
*/
protected function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return;
}
if (!is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return;
}
// Redirect disabled users to a info page
if (!$user->isEnabled() && !$this instanceof InfoController) {
return $this->redirectToRoute('path_to_info');
}
return $user;
}