什么是在访问控制规则重定向后通知用户的最佳方式?

22

来自 Symfony 2.3 Security 文档:

如果访问被拒绝,则系统会尝试对用户进行身份验证(例如将用户重定向到登录页面)。如果用户已经登录,则显示403“拒绝访问”错误页面。有关更多信息,请参见如何自定义错误页面。

我目前正在为一些路由使用access_control规则。我想通知匿名用户,如果他们被重定向到登录路由,则显示类似“您必须登录才能访问该页面”的消息。 我已经多次阅读了安全文档,但没有找到任何相关内容。我是不是忽略了什么?

如果没有,当access_control规则阻止用户时,最佳方法是仅通知用户(即当用户被重定向到登录页面时而不是在未授权角色中时)?

编辑: 为了澄清,我特别询问如何检查重定向是否是由access_control规则引起的(最好是在Twig中)。

4个回答

62

经过一番研究,我找到了正确的方法。您需要使用入口点服务,并在防火墙配置中定义它。

这种方法不会影响您在防火墙配置中指定的默认页面设置以进行登录。


代码

security.yml:

firewalls:
    main:
        entry_point: entry_point.user_login #or whatever you name your service
        pattern: ^/
        form_login:
        # ...

src/Acme/UserBundle/config/services.yml

services:
    entry_point.user_login:
        class: Acme\UserBundle\Service\LoginEntryPoint
        arguments: [ @router ] #I am going to use this for URL generation since I will be redirecting in my service

src/Acme/UserBundle/Service/LoginEntryPoint.php:

namespace Acme\UserBundle\Service;

use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface,
    Symfony\Component\Security\Core\Exception\AuthenticationException,
    Symfony\Component\HttpFoundation\Request,
    Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * When the user is not authenticated at all (i.e. when the security context has no token yet), 
 * the firewall's entry point will be called to start() the authentication process. 
 */
class LoginEntryPoint implements AuthenticationEntryPointInterface
{
    protected $router;

    public function __construct($router)
    {
        $this->router = $router;
    }

    /*
     * This method receives the current Request object and the exception by which the exception 
     * listener was triggered. 
     * 
     * The method should return a Response object
     */
    public function start(Request $request, AuthenticationException $authException = null)
    {
        $session = $request->getSession();

        // I am choosing to set a FlashBag message with my own custom message.
        // Alternatively, you could use AuthenticationException's generic message 
        // by calling $authException->getMessage()
        $session->getFlashBag()->add('warning', 'You must be logged in to access that page');

        return new RedirectResponse($this->router->generate('login'));
    }
}

login.html.twig:

{# bootstrap ready for your convenience ;] #}
{% if app.session.flashbag.has('warning') %}
    {% for flashMessage in app.session.flashbag.get('warning') %}
        <div class="alert alert-warning">
            <button type="button" class="close" data-dismiss="alert">&times;</button>
            {{ flashMessage }}
        </div>
    {% endfor %}
{% endif %}

资源:


6
好的,凯莉,真是场噩梦! - Jimbo
1
干得好。省了我很多时间! - Ronnie Jespersen
1
虽然不是我所期望的,但还是对类似问题非常有帮助!谢谢! - Alex

1
我认为一个kernel.exception监听器和设置一个闪存消息可以达到目的。未经测试的示例:
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class My403ExceptionListener
{
    protected $session;

    public function __construct(SessionInterface $session)
    {
        $this->session = $session;
    }

    public function onKernelException(GetResponseForExceptionEvent $event)
    {
        $exception = $event->getException();
        if ($exception instanceof AccessDeniedHttpException) {
            $this->session->getFlashBag()->set('warning', 'You must login to access that page.');
        }
    }
}

我不确定它是否有效或是否正确。您可以将其注册为kernel.event_listener。或者,您可以编写一个专用的服务,并将其设置为防火墙配置access_denied_handler的参数。我认为有许多可能的方法。


access_denied_handler只有在未经授权时才会被触发,对于重定向则不会。 - Carrie Kendall
是的,它在搜索结果中非常难以理解,而且文档对其描述也非常简略。我可能会提交一个补丁。 - Carrie Kendall

0

我采用了不同的方法,在控制器中,我会抛出新的AuthenticationException异常,并在此之前将错误消息添加到 Flashbag 中。

// Check if user is logged in.
if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED') == FALSE) {
  $request->getSession()
    ->getFlashBag()
    ->add('error', "Please login to continue");
  throw new AuthenticationException();
}

在登录 Twig 模板中,显示消息。
{% for flash_message in app.session.flashBag.get('error') %}
  <div class="callout alert" role="alert">
    {{ flash_message }}
  </div>
{% endfor %}

0

你能不能只设置两个登录路由?

例如,在安全配置中设置

form_login:
    login_path: /login_message

在您的登录控制器中。
/**
 * @Template()
 * @Route("/notauthorized", name="login_message")
 */
 public function loginMessageAction()
 {
    return [];
 }

然后在您的loginMessage.html.twg文件中

<a href="{{ path('login') }}">You must login to access this page.</a>

1
虽然这是一种可能性,但我特别寻找一种处理访问控制规则的方法。这只是一个解决方法,而不是真正的解决方案。 - Carrie Kendall
当我写这段代码时,我也有同样的感觉。我相信有一种“非hackish”的方法来解决它,只是我不确定怎么做。 - Kris
请检查我的答案,以获得一个干净、无黑客攻击的解决方案 :D - Carrie Kendall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接