简体   繁体   中英

What is the best way to notify a user after an access_control rule redirects?

From Symfony 2.3 Security docs:

If access is denied, the system will try to authenticate the user if not already (eg redirect the user to the login page) . If the user is already logged in, the 403 "access denied" error page will be shown. See How to customize Error Pages for more information.

I am currently using an access_control rule for a few routes. I would like to notify an anonymous user if they're redirected to the login route with a message like " You must login to access that page ." I have read through the Security docs a few times and haven't found anything relevant to this. Am I overlooking something?

If not, what would be the best way to notify the user when they're stopped by an access_control rule only if they're redirected to login (ie not if they're just in an unauthorized role )?

EDIT: For clarification, I am specifically asking how to check if a redirect was caused by an access_control rule (preferably in twig if possible).

So after quite a bit of research, I found the right way to do this. You'll need to use an Entry Point service and define it in your firewall configuration.

This method will not mess with your default page settings specified in your firewall config for logging in.


The Code

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 %}

Resources:

I think a kernel.exception listener and setting a flash message can do it. Untested example:

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.');
        }
    }
}

Don't really know if it works or if it's the right thing. You can register it as kernel.event_listener . Or maybe it's better you wright a dedicated service and set it as the parameter of access_denied_handler in the firewall config . I think there a many possible ways.

Could you not just have two login routes?

For example, in security config set

form_login:
    login_path: /login_message

In your login controller

/**
 * @Template()
 * @Route("/notauthorized", name="login_message")
 */
 public function loginMessageAction()
 {
    return [];
 }

And then in your loginMessage.html.twg

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

I'm following different approach, in controller I'm throwing new AuthenticationException and before that adding error message in 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();
}

And in login twig template, showing messages.

{% for flash_message in app.session.flashBag.get('error') %}
  <div class="callout alert" role="alert">
    {{ flash_message }}
  </div>
{% endfor %}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM