1
1
.. index ::
2
2
single: Security; Creating a Custom Access Denied Handler
3
3
4
- How to Create a Custom Access Denied Handler
5
- ============================================
4
+ How to Customize Access Denied Responses
5
+ ========================================
6
6
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:
9
11
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
11
109
: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)::
15
114
16
115
namespace App\Security;
17
116
@@ -49,11 +148,21 @@ configure it under your firewall:
49
148
.. code-block :: xml
50
149
51
150
<!-- 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 >
57
166
58
167
.. code-block :: php
59
168
@@ -69,5 +178,47 @@ configure it under your firewall:
69
178
],
70
179
]);
71
180
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 (1), 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 other exception listeners from being called)
222
+ //$event->stopPropagation();
223
+ }
224
+ }
0 commit comments