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
11
- :class: `Symfony\\ Component\\ Security\\ Http\\ Authorization\\ AccessDeniedHandlerInterface `.
12
- This interface defines one method called ``handle() `` where you can implement whatever
13
- logic that should execute when access is denied for the current user (e.g. send a
14
- mail, log a message, or generally return a custom response)::
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
+ To customize the handling of unauthenticated users, create a class that
22
+ implements
23
+ :class: `Symfony\\ Component\\ Security\\ Http\\ EntryPoint\\ AuthenticationEntryPointInterface `.
24
+ This interface has one method (``start() ``) that is called whenever an
25
+ unauthenticated user tries to access a protected resource::
26
+
27
+ namespace App\Security;
28
+
29
+ use Symfony\Component\HttpFoundation\Request;
30
+ use Symfony\Component\HttpFoundation\RedirectResponse;
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 flashbag 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
109
+ :class: `Symfony\\ Component\\ Security\\ Http\\ Authorization\\ AccessDeniedHandlerInterface `
110
+ to customize the access denied response for authenticated users. This
111
+ interface defines one method called ``handle() `` where you can implement
112
+ whatever logic that should execute when access is denied for the current
113
+ user (e.g. send a mail, log a message, or generally return a custom
114
+ response)::
15
115
16
116
namespace App\Security;
17
117
@@ -49,11 +149,21 @@ configure it under your firewall:
49
149
.. code-block :: xml
50
150
51
151
<!-- config/packages/security.xml -->
52
- <config >
53
- <firewall name =" main" >
54
- <access-denied-handler >App\Security\AccessDeniedHandler</access-denied-handler >
55
- </firewall >
56
- </config >
152
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
153
+ <srv : container xmlns =" http://symfony.com/schema/dic/security"
154
+ xmlns : xsi =" http://www.w3.org/2001/XMLSchema-instance"
155
+ xmlns : srv =" http://symfony.com/schema/dic/services"
156
+ xsi : schemaLocation =" http://symfony.com/schema/dic/services
157
+ https://symfony.com/schema/dic/services/services-1.0.xsd" >
158
+
159
+ <config >
160
+ <firewall name =" main"
161
+ access-denied-handler =" App\Security\AccessDeniedHandler"
162
+ >
163
+ <!-- ... -->
164
+ </firewall >
165
+ </config >
166
+ </srv : container >
57
167
58
168
.. code-block :: php
59
169
@@ -69,5 +179,42 @@ configure it under your firewall:
69
179
],
70
180
]);
71
181
72
- That's it! Any ``AccessDeniedException `` thrown by code under the ``main `` firewall
73
- will now be handled by your service.
182
+ Customizing All Access Denied Responses
183
+ ---------------------------------------
184
+
185
+ In some cases, you might want to customize both responses or do a specific
186
+ action (e.g. logging) for each ``AccessDeniedException ``. In this case,
187
+ configure a :ref: `kernel.exception listener <use-kernel-exception-event >`::
188
+
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 >1, to make sure it's called before
203
+ // the default exception listener
204
+ KernelEvents::EXCEPTION => ['onKernelException', 2],
205
+ ];
206
+ }
207
+
208
+ public function onKernelException(ExceptionEvent $event): void
209
+ {
210
+ $exception = $event->getException();
211
+ if (!$exception instanceof AccessDeniedException) {
212
+ return;
213
+ }
214
+
215
+ // ... perform some action (e.g. logging)
216
+
217
+ // optionally set the custom response
218
+ $event->setResponse(new Response(null, 403));
219
+ }
220
+ }
0 commit comments