11.. index ::
22 single: Security; Creating a Custom Access Denied Handler
33
4- How to Create a Custom Access Denied Handler
5- ============================================
4+ How to Customize Access Denied Responses
5+ ========================================
66
7- When your application throws an ``AccessDeniedException ``, you can handle this exception
8- with a service to return a custom response.
7+ In Symfony, you can throw an
8+ :class: `Symfony\\ Component\\ Security\\ Core\\ Exception\\ AccessDeniedException `
9+ to disallow access to the user. Symfony will handle this exception and
10+ generates a response based on the authentication state:
911
10- First, create a class that implements
12+ * **If the user is not authenticated ** (or authenticated anonymously), an
13+ authentication entry point is used to generated a response (typically
14+ a redirect to the login page or an *401 Unauthorized * response);
15+ * **If the user is authenticated, but does not have the required
16+ permissions **, a *403 Forbidden * response is generated.
17+
18+ Customize the Unauthorized Response
19+ -----------------------------------
20+
21+ You need to create a class that implements
22+ :class: `Symfony\\ Component\\ Security\\ Http\\ EntryPoint\\ AuthenticationEntryPointInterface `.
23+ This interface has one method (``start() ``) that is called whenever an
24+ unauthenticated user tries to access a protected resource::
25+
26+ // src/Security/AuthenticationEntryPoint.php
27+ namespace App\Security;
28+
29+ use Symfony\Component\HttpFoundation\RedirectResponse;
30+ use Symfony\Component\HttpFoundation\Request;
31+ use Symfony\Component\HttpFoundation\Session\SessionInterface;
32+ use Symfony\Component\Security\Core\Exception\AuthenticationException;
33+ use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
34+
35+ class AuthenticationEntryPoint implements AuthenticationEntryPointInterface
36+ {
37+ private $urlGenerator;
38+ private $session;
39+
40+ public function __construct(UrlGeneratorInterface $urlGenerator, SessionInterface $session)
41+ {
42+ $this->urlGenerator = $urlGenerator;
43+ $this->session = $session;
44+ }
45+
46+ public function start(Request $request, AuthenticationException $authException = null): RedirectResponse
47+ {
48+ // add a custom flash message and redirect to the login page
49+ $this->session->getFlashBag()->add('note', 'You have to login in order to access this page.');
50+
51+ return new RedirectResponse($this->urlGenerator->generate('security_login'));
52+ }
53+ }
54+
55+ That's it if you're using the :ref: `default services.yaml configuration <service-container-services-load-example >`.
56+ Otherwise, you have to register this service in the container.
57+
58+ Now, configure this service ID as the entry point for the firewall:
59+
60+ .. configuration-block ::
61+
62+ .. code-block :: yaml
63+
64+ # config/packages/security.yaml
65+ firewalls :
66+ # ...
67+
68+ main :
69+ # ...
70+ entry_point : App\Security\AuthenticationEntryPoint
71+
72+ .. code-block :: xml
73+
74+ <!-- config/packages/security.xml -->
75+ <?xml version =" 1.0" encoding =" UTF-8" ?>
76+ <srv : container xmlns =" http://symfony.com/schema/dic/security"
77+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
78+ xmlns : srv =" http://symfony.com/schema/dic/services"
79+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
80+ https://symfony.com/schema/dic/services/services-1.0.xsd" >
81+
82+ <config >
83+ <firewall name =" main"
84+ entry-point =" App\Security\AuthenticationEntryPoint"
85+ >
86+ <!-- ... -->
87+ </firewall >
88+ </config >
89+ </srv : container >
90+
91+ .. code-block :: php
92+
93+ // config/packages/security.php
94+ use App\Security\AuthenticationEntryPoint;
95+
96+ $container->loadFromExtension('security', [
97+ 'firewalls' => [
98+ 'main' => [
99+ // ...
100+ 'entry_point' => AuthenticationEntryPoint::class,
101+ ],
102+ ],
103+ ]);
104+
105+ Customize the Forbidden Response
106+ --------------------------------
107+
108+ Create a class that implements
11109:class: `Symfony\\ Component\\ Security\\ Http\\ Authorization\\ AccessDeniedHandlerInterface `.
12- This interface defines one method called ``handle() `` where you can implement whatever
13- logic that should run when access is denied for the current user (e.g. send a
14- mail, log a message, or generally return a custom response)::
110+ This interface defines one method called ``handle() `` where you can
111+ implement whatever logic that should execute when access is denied for the
112+ current user (e.g. send a mail, log a message, or generally return a custom
113+ response)::
15114
16115 namespace App\Security;
17116
@@ -49,11 +148,21 @@ configure it under your firewall:
49148 .. code-block :: xml
50149
51150 <!-- config/packages/security.xml -->
52- <config >
53- <firewall name =" main" >
54- <access-denied-handler >App\Security\AccessDeniedHandler</access-denied-handler >
55- </firewall >
56- </config >
151+ <?xml version =" 1.0" encoding =" UTF-8" ?>
152+ <srv : container xmlns =" http://symfony.com/schema/dic/security"
153+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
154+ xmlns : srv =" http://symfony.com/schema/dic/services"
155+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
156+ https://symfony.com/schema/dic/services/services-1.0.xsd" >
157+
158+ <config >
159+ <firewall name =" main"
160+ access-denied-handler =" App\Security\AccessDeniedHandler"
161+ >
162+ <!-- ... -->
163+ </firewall >
164+ </config >
165+ </srv : container >
57166
58167 .. code-block :: php
59168
@@ -69,5 +178,47 @@ configure it under your firewall:
69178 ],
70179 ]);
71180
72- That's it! Any ``AccessDeniedException `` thrown by code under the ``main `` firewall
73- will now be handled by your service.
181+ Customizing All Access Denied Responses
182+ ---------------------------------------
183+
184+ In some cases, you might want to customize both responses or do a specific
185+ action (e.g. logging) for each ``AccessDeniedException ``. In this case,
186+ configure a :ref: `kernel.exception listener <use-kernel-exception-event >`::
187+
188+ // src/EventListener/AccessDeniedListener.php
189+ namespace App\EventListener;
190+
191+ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
192+ use Symfony\Component\HttpFoundation\Response;
193+ use Symfony\Component\HttpKernel\Event\ExceptionEvent;
194+ use Symfony\Component\HttpKernel\KernelEvents;
195+ use Symfony\Component\Security\Core\Exception\AccessDeniedException;
196+
197+ class AccessDeniedListener implements EventSubscriberInterface
198+ {
199+ public static function getSubscribedEvents(): array
200+ {
201+ return [
202+ // the priority must be greater than the Security HTTP
203+ // ExceptionListener, to make sure it's called before
204+ // the default exception listener
205+ KernelEvents::EXCEPTION => ['onKernelException', 2],
206+ ];
207+ }
208+
209+ public function onKernelException(ExceptionEvent $event): void
210+ {
211+ $exception = $event->getException();
212+ if (!$exception instanceof AccessDeniedException) {
213+ return;
214+ }
215+
216+ // ... perform some action (e.g. logging)
217+
218+ // optionally set the custom response
219+ $event->setResponse(new Response(null, 403));
220+
221+ // or stop propagation (prevents the next exception listeners from being called)
222+ //$event->stopPropagation();
223+ }
224+ }
0 commit comments