@@ -10,7 +10,7 @@ Security
10
10
Do you prefer video tutorials? Check out the `Symfony Security screencast series `_.
11
11
12
12
Symfony's security system is incredibly powerful, but it can also be confusing
13
- to set up. But don 't worry! In this article, you'll learn how to set up your app's
13
+ to set up. Don 't worry! In this article, you'll learn how to set up your app's
14
14
security system step-by-step:
15
15
16
16
#. :ref: `Installing security support <security-installation >`;
@@ -41,12 +41,12 @@ install the security feature before using it:
41
41
.. _initial-security-yaml-setup-authentication :
42
42
.. _create-user-class :
43
43
44
- 2 ) Create your User Class
45
- -------------------------
44
+ 2a ) Create your User Class
45
+ --------------------------
46
46
47
47
No matter *how * you will authenticate (e.g. login form or API tokens) or *where *
48
- your user data will be stored (database, SSO ), the next step is always the same:
49
- create a "User" class. The easiest way is to use `MakerBundle `_.
48
+ your user data will be stored (database, single sign-on ), the next step is always the same:
49
+ create a "User" class. The easiest way is to use the `MakerBundle `_.
50
50
51
51
Let's assume that you want to store your user data in the database with Doctrine:
52
52
@@ -122,7 +122,8 @@ command will pre-configure this for you:
122
122
# ...
123
123
124
124
encoders :
125
- Symfony\Component\Security\Core\User\User :
125
+ # use your user class name here
126
+ App\Entity\User :
126
127
# bcrypt or argon21 are recommended
127
128
# argon21 is more secure, but requires PHP 7.2 or the Sodium extension
128
129
algorithm : bcrypt
@@ -141,7 +142,7 @@ command will pre-configure this for you:
141
142
<config >
142
143
<!-- ... -->
143
144
144
- <encoder class =" Symfony\Component\Security\Core\User \User"
145
+ <encoder class =" App\Entity \User"
145
146
algorithm =" bcrypt"
146
147
cost =" 12" />
147
148
@@ -156,7 +157,7 @@ command will pre-configure this for you:
156
157
// ...
157
158
158
159
'encoders' => array(
159
- 'Symfony\Component\Security\Core\User \User' => array(
160
+ 'App\Entity \User' => array(
160
161
'algorithm' => 'bcrypt',
161
162
'cost' => 12,
162
163
)
@@ -210,7 +211,7 @@ Use this service to encode the passwords:
210
211
}
211
212
}
212
213
213
- Of, you can manually encode a password by running:
214
+ You can manually encode a password by running:
214
215
215
216
.. code-block :: terminal
216
217
@@ -220,8 +221,8 @@ Of, you can manually encode a password by running:
220
221
.. _security-firewalls :
221
222
.. _firewalls-authentication :
222
223
223
- 3 ) Authentication & Firewalls
224
- -----------------------------
224
+ 3a ) Authentication & Firewalls
225
+ ------------------------------
225
226
226
227
The security system is configured in ``config/packages/security.yaml ``. The *most *
227
228
important section is ``firewalls ``:
@@ -290,7 +291,8 @@ Nope, thanks to the ``anonymous`` key, this firewall *is* accessible anonymously
290
291
291
292
In fact, if you go to the homepage right now, you *will * have access and you'll see
292
293
that you're "authenticated" as ``anon. ``. Don't be fooled by the "Yes" next to
293
- Authenticated, you're just an anonymous user:
294
+ Authenticated. The firewall verified that it does not know your identity, and so,
295
+ you are anonymous:
294
296
295
297
.. image :: /_images/security/anonymous_wdt.png
296
298
:align: center
@@ -299,7 +301,7 @@ You'll learn later how to deny access to certain URLs or controllers.
299
301
300
302
.. note ::
301
303
302
- If you do not see toolbar, install the :doc: `profiler </profiler >` with:
304
+ If you do not see the toolbar, install the :doc: `profiler </profiler >` with:
303
305
304
306
.. code-block :: terminal
305
307
@@ -348,7 +350,7 @@ For the most detailed description of authenticators and how they work, see
348
350
4) Denying Access, Roles and other Authorization
349
351
------------------------------------------------
350
352
351
- Users can now login to your app using your login form. Great! Now, you need to learn
353
+ Users can now log in to your app using your login form. Great! Now, you need to learn
352
354
how to deny access and work with the User object. This is called **authorization **,
353
355
and its job is to decide if a user can access some resource (a URL, a model object,
354
356
a method call, ...).
@@ -364,7 +366,7 @@ Roles
364
366
~~~~~
365
367
366
368
When a user logs in, Symfony calls the ``getRoles() `` method on your ``User ``
367
- object to determime which roles this user has. In the ``User `` class that we
369
+ object to determine which roles this user has. In the ``User `` class that we
368
370
generated earlier, the roles are an array that's stored in the database, and
369
371
every user is *always * given at least one role: ``ROLE_USER ``::
370
372
@@ -388,16 +390,14 @@ every user is *always* given at least one role: ``ROLE_USER``::
388
390
This is a nice default, but you can do *whatever * you want to determine which roles
389
391
a user should have. Here are a few guidelines:
390
392
391
- * Every role **must start with ** ``ROLE_ `` (otherwise, things won't as expected)
393
+ * Every role **must start with ** ``ROLE_ `` (otherwise, things won't work as expected)
392
394
393
395
* Other than the above rule, a role is just a string and you can invent what you
394
- need (e.g. ``ROLE_PRODUCT_ADMIN ``)
395
-
396
- * Every User **must ** have at least **one ** role - a common convention is to give
397
- *every * user ``ROLE_USER ``.
396
+ need (e.g. ``ROLE_PRODUCT_ADMIN ``).
398
397
398
+ You'll use these roles next to grant access to specific sections of your site.
399
399
You can also use a :ref: `role hierarchy <security-role-hierarchy >` where having
400
- some roles automatically gives you other roles.
400
+ some roles automatically give you other roles.
401
401
402
402
.. _security-role-authorization :
403
403
@@ -531,7 +531,7 @@ Prepending the path with ``^`` means that only URLs *beginning* with the
531
531
pattern are matched. For example, a path of simply ``/admin `` (without
532
532
the ``^ ``) would match ``/admin/foo `` but would also match URLs like ``/foo/admin ``.
533
533
534
- Each ``access_control `` can also match on IP address, host name and HTTP methods.
534
+ Each ``access_control `` can also match on IP address, hostname and HTTP methods.
535
535
It can also be used to redirect a user to the ``https `` version of a URL pattern.
536
536
See :doc: `/security/access_control `.
537
537
@@ -550,15 +550,15 @@ You can easily deny access from inside a controller::
550
550
$this->denyAccessUnlessGranted('ROLE_ADMIN');
551
551
552
552
// or add an optional message - seen by developers
553
- $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page! ');
553
+ $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'User tried to access a page without having ROLE_ADMIN ');
554
554
}
555
555
556
556
That's it! If access is not granted, a special
557
557
:class: `Symfony\\ Component\\ Security\\ Core\\ Exception\\ AccessDeniedException `
558
558
is thrown and no more code in your controller is executed. Then, one of two things
559
559
will happen:
560
560
561
- 1) If the user isn't logged in yet, they will be asked to login (e.g. redirected
561
+ 1) If the user isn't logged in yet, they will be asked to log in (e.g. redirected
562
562
to the login page).
563
563
564
564
2) If the user *is * logged in, but does *not * have the ``ROLE_ADMIN `` role, they'll
@@ -637,7 +637,7 @@ You can use ``IS_AUTHENTICATED_FULLY`` anywhere roles are used: like ``access_co
637
637
or in Twig.
638
638
639
639
``IS_AUTHENTICATED_FULLY `` isn't a role, but it kind of acts like one, and every
640
- user that has logged in will have this. ACtually , there are 3 special attributes
640
+ user that has logged in will have this. Actually , there are 3 special attributes
641
641
like this:
642
642
643
643
* ``IS_AUTHENTICATED_REMEMBERED ``: *All * logged in users have this, even
@@ -671,8 +671,8 @@ If you still prefer to use traditional ACLs, refer to the `Symfony ACL bundle`_.
671
671
672
672
.. _retrieving-the-user-object :
673
673
674
- 5 ) Fetching the User Object
675
- ---------------------------
674
+ 5a ) Fetching the User Object
675
+ ----------------------------
676
676
677
677
After authentication, the ``User `` object of the current user can be accessed
678
678
via the ``getUser() `` shortcut::
@@ -683,14 +683,16 @@ via the ``getUser()`` shortcut::
683
683
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
684
684
685
685
// returns your User object, or null if the user is not authenticated
686
+ // use inline documentation to tell your editor your exact User class
687
+ /** @var \App\Entity\User $user */
686
688
$user = $this->getUser();
687
689
688
690
// Call whatever methods you've added to your User class
689
691
// For example, if you added a getFirstName() method, you can use that.
690
692
return new Response('Well hi there '.$user->getFirstName());
691
693
}
692
694
693
- 5B ) Fetching the User from a Service
695
+ 5b ) Fetching the User from a Service
694
696
------------------------------------
695
697
696
698
If you need to get the logged in user from a service, use the
@@ -707,6 +709,8 @@ If you need to get the logged in user from a service, use the
707
709
708
710
public function __construct(Security $security)
709
711
{
712
+ // Avoid calling getUser() in the constructor: auth may not
713
+ // be complete yet. Instead, store the entire Security object.
710
714
$this->security = $security;
711
715
}
712
716
734
738
Logging Out
735
739
-----------
736
740
737
- To add logout , activate the ``logout `` config parameter under your firewall:
741
+ To enable logging out , activate the ``logout `` config parameter under your firewall:
738
742
739
743
.. configuration-block ::
740
744
@@ -808,11 +812,12 @@ Next, you'll need to create a route for this URL (but not a controller):
808
812
class SecurityController extends AbstractController
809
813
{
810
814
/**
811
- * @Route("/login ", name="app_logout")
815
+ * @Route("/logout ", name="app_logout")
812
816
*/
813
817
public function logout()
814
818
{
815
- // controller should be blank - will never be executed
819
+ // controller can be blank: it will never be executed!
820
+ throw new \Exception('Don\'t forget to activate logout in security.yaml');
816
821
}
817
822
}
818
823
@@ -905,9 +910,18 @@ Users with the ``ROLE_ADMIN`` role will also have the
905
910
``ROLE_USER `` role. And users with ``ROLE_SUPER_ADMIN ``, will automatically have
906
911
``ROLE_ADMIN ``, ``ROLE_ALLOWED_TO_SWITCH `` and ``ROLE_USER `` (inherited from ``ROLE_ADMIN ``).
907
912
913
+ For role hierarchy to work, do not try to call ``$user->getRoles() `` manually::
914
+
915
+ // BAD - $user->getRoles() will not know about the role hierarchy
916
+ $hasAccess = in_array('ROLE_ADMIN', $user->getRoles());
917
+
918
+ // GOOD - use of the normal security methods
919
+ $hasAccess = $this->isGranted('ROLE_ADMIN');
920
+ $this->denyAccessUnlessGranted('ROLE_ADMIN');
921
+
908
922
.. note ::
909
923
910
- The ``role_hierarchy `` values iare static - you can't, for example, store the
924
+ The ``role_hierarchy `` values are static - you can't, for example, store the
911
925
role hierarchy in a database. If you need that, create a custom
912
926
:doc: `security voter </security/voters >` that looks for the user roles
913
927
in the database.
@@ -923,7 +937,9 @@ Frequently Asked Questions
923
937
**Can I have Multiple Firewalls? **
924
938
Yes! But it's usually not necessary. Each firewall is like a separate security
925
939
system. And so, unless you have *very * different authentication needs, one
926
- firewall usually works well.
940
+ firewall usually works well. With :doc: `Guard authentication </security/guard_authentication >`,
941
+ you can create various, diverse ways of allowing authentication (e.g. form login,
942
+ API key authentication and LDAP) all under the same firewall.
927
943
928
944
**Can I Share Authentication Between Firewalls? **
929
945
Yes, but only with some configuration. If you're using multiple firewalls and
@@ -942,11 +958,15 @@ Frequently Asked Questions
942
958
**My Authentication Doesn't Seem to Work: No Errors, but I'm Never Logged In **
943
959
Sometimes authentication may be successful, but after redirecting, you're
944
960
logged out immediately due to a problem loading the ``User `` from the session.
945
- To see if this is the issue, temporarily enable :ref: `intercept_redirects `.
946
- Then, when you login, instead of being redirected, you'll be stopped. Check
947
- the web debug toolbar on that page to see if you're logged in. If you *are *,
948
- but are no longer logged in after redirecting, then there is a problem loading
949
- your User from the session. See :ref: `user_session_refresh `.
961
+ To see if this is an issue, check your log file (``var/log/dev.log ``) for
962
+ the log message:
963
+
964
+ > Cannot refresh token because user has changed.
965
+
966
+ If you see this, there are two possible causes. First, there may be a problem
967
+ loading your User from the session. See :ref: `user_session_refresh `. Second,
968
+ if certain user information was changed in the database since the last page
969
+ refresh, Symfony will purposely log out the user for security reasons.
950
970
951
971
Learn More
952
972
----------
0 commit comments