If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Let's set some plain passwords! Where? In our fixtures! Open up fixtures.yml
and
scroll down. In theory, all we need to do is set the plainPassword
property.
The rest should happen auto-magically.
Add plainPassword:
and we'll keep with iliketurtles
, because I've gotten good
at typing that. And turtles are cool:
... lines 1 - 22 | |
AppBundle\Entity\User: | |
user_{1..10}: | |
... line 25 | |
plainPassword: iliketurtles |
Change over to your terminal and load your fixtures:
./bin/console doctrine:fixtures:load
Explosion!
No encoder has been configured for AppBundle\Entity\User
This is basically Symfony's way of saying:
Ryan, you didn't tell me how you want to encode the passwords. I can't read your mind - I'm just a PHP framework.
Remember how I kept saying we would encrypt the passwords with bcrypt
? Do you remember
actually configuring that anywhere? Nope! We need to do that.
Open security.yml
. Add an encoders
key, then AppBundle\Entity\User: bcrypt
:
... lines 1 - 2 | |
security: | |
encoders: | |
AppBundle\Entity\User: bcrypt | |
... lines 6 - 33 |
If you want, you can configure a few other options, but this is good enough.
Now Symfony knows how to encrypt our passwords. Try the fixtures again:
./bin/console doctrine:fixtures:load
No errors! So, that probably worked. Let's use it!
In LoginFormAuthenticator
, we can finally add some real password checking!
How? By using that same UserPasswordEncoder
object.
Head back up to __construct
and add a new UserPasswordEncoder
argument. I'll
use my Option
+Enter
shortcut to setup that property for me:
... lines 1 - 10 | |
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoder; | |
... lines 12 - 15 | |
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator | |
{ | |
... lines 18 - 20 | |
private $passwordEncoder; | |
public function __construct(FormFactoryInterface $formFactory, EntityManager $em, RouterInterface $router, UserPasswordEncoder $passwordEncoder) | |
{ | |
... lines 25 - 27 | |
$this->passwordEncoder = $passwordEncoder; | |
} | |
... lines 30 - 78 | |
} |
And because we're using autowiring for this service, we don't need to change anything
in services.yml
.
Now, in checkCredentials()
, replace the if
statement with
if ($this->passwordEncoder->isPasswordValid())
and pass that the User
object
and the plain-text password:
... lines 1 - 15 | |
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator | |
{ | |
... lines 18 - 58 | |
public function checkCredentials($credentials, UserInterface $user) | |
{ | |
... lines 61 - 62 | |
if ($this->passwordEncoder->isPasswordValid($user, $password)) { | |
return true; | |
} | |
... lines 66 - 67 | |
} | |
... lines 69 - 78 | |
} |
That'll take care of securely checking things.
Let's try it out: head to /login
. Use weaverryan+1@gmail.com
and iliketurtles
.
We're in! Password system check.
Ok team, that's it for authentication. You can build your authenticator to behave
however you want, and you can even have multiple authenticators. Oh, and if you
do want to use any of the built-in authentication systems, like the form_login
key I mentioned earlier - that's totally fine. Guard authentication takes more work,
but has more flexibility. If you want another example, we created a cool JSON web
token authenticator in our Symfony REST series.
Now, let's start locking down the system.
Hey Juan carlos
I just downloaded the course code and I can see the "+" on that line (and it is on the code blocks as well). Could you double check it?
Cheers!
If you are having issues with your event listener picking up the hash password listener in 3.4
Your service yml should look like this so that the fixtures generate the hashed passwords.
app.doctrine.hash_password_listener:
class: AppBundle\Doctrine\HashPasswordListener
autowire: true
tags:
- { name: doctrine.event_listener, event: prePersist, lazy: true }
Hey Sara!
Your code WILL totally work. But, the code in the tutorial should work too. Does your HashPasswordListener implement the EventSubscriber interface? Basically, there are 2 valid ways to create "listeners" - the tutorial shows the "subscriber" method. You are (actually, perfectly) showing the other method or configuring it.
Oh, and the lazy: true part is not needed, but a nice idea - it gives you a small performance boost when Doctrine is working, but your listener is not actually needed. Good find ;). In Symfony 4.2, that config will be ON by default... because it's just THAT awesome :p.
Cheers!
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.1.*", // v3.1.4
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.6.4
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
"symfony/swiftmailer-bundle": "^2.3", // v2.3.11
"symfony/monolog-bundle": "^2.8", // 2.11.1
"symfony/polyfill-apcu": "^1.0", // v1.2.0
"sensio/distribution-bundle": "^5.0", // v5.0.22
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
"incenteev/composer-parameter-handler": "^2.0", // v2.1.2
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
"doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.0.7
"symfony/phpunit-bridge": "^3.0", // v3.1.3
"nelmio/alice": "^2.1", // 2.1.4
"doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
}
}
Hi guys, there is an '+' missing on line src/AppBundle/DataFixtures/ORM/fixtures.yml:25. Excelento work!