In this article, you'll learn how to set up user authentication in php using the Symfony Security component. As well as authentication, I'll show you how to use its role-based authorization, which you can extend according to your needs.
The Symfony Security ComponentThe Symfony Security Component allows you to set up security features like authentication, role-based authorization, CSRF tokens and more very easily. In fact, it's further divided into four sub-components which you can choose from according to your needs.
The Security component has the following sub-components:
symfony/security-core symfony/security-http symfony/security-csrf symfony/security-aclIn this article, we are going to explore the authentication feature provided by the symfony/security-core component.
As usual, we'll start with the installation and configuration instructions, and then we'll explore a few real-world examples to demonstrate the key concepts.
Installation and ConfigurationIn this section, we are going to install the Symfony Security component. I assume that you have already installed Composer on your system―we'll need it to install the Security component available at Packagist.
So go ahead and install the Security component using the following command.
$composer require symfony/securityWe are going to load users from the mysql database in our example, so we'll also need a database abstraction layer. Let's install one of the most popular database abstraction layers: Doctrine DBAL.
$composer require doctrine/dbalThat should have created the composer.json file, which should look like this:
{"require": {
"symfony/security": "^4.1",
"doctrine/dbal": "^2.7"
}
}
Let's modify the composer.json file to look like the following one.
{"require": {
"symfony/security": "^4.1",
"doctrine/dbal": "^2.7"
},
"autoload": {
"psr-4": {
"Sfauth\\": "src"
},
"classmap": ["src"]
}
}
As we have added a new classmap entry, let's go ahead and update the composer autoloader by running the following command.
$composer dump -oNow, you can use the Sfauth namespace to autoload classes under the src directory.
So that's the installation part, but how are you supposed to use it? In fact, it's just a matter of including the autoload.php file created by Composer in your application, as shown in the following snippet.
<?phprequire_once './vendor/autoload.php';
// application code
?> A Real-World Example
Firstly, let's go through the usual authentication flow provided by the Symfony Security component.
UserInterfaceIn our example, we are going to match the user credentials against the MySQL database, thus we'll need to create the database user provider. We'll also create the database authentication provider that handles the authentication logic. And finally, we'll create the User class, which implements the UserInterface interface.
The User ClassIn this section, we'll create the User class which represents the user entity in the authentication process.
Go ahead and create the src/User/User.php file with the following contents.
<?phpnamespace Sfauth\User;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface
{
private $username;
private $password;
private $roles;
public function __construct(string $username, string $password, string $roles)
{
if (empty($username))
{
throw new \InvalidArgumentException('No username provided.');
}
$this->username = $username;
$this->password = $password;
$this->roles = $roles;
}
public function getUsername()
{
return $this->username;
}
public function getPassword()
{
return $this->password;
}
public function getRoles()
{
return explode(",", $this->roles);
}
public function getSalt()
{
return '';
}
public function eraseCredentials() {}
}
The important thing is that the User class must implement the Symfony Security UserInterface interface. Apart from that, there's nothing out of the ordinary here.
The Database Provider ClassIt's the responsibility of the user provider to load users from the back-end. In this section, we'll create the database user provider, which loads the user from the MySQL database.
Let's create the src/User/DatabaseUserProvider.php file with the following contents.
<?phpnamespace Sfauth\User;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\DBAL\Connection;
use Sfauth\User\User;
class DatabaseUserProvider implements UserProviderInterface
{
private $connection;
public function __construct(Connection $connection)
{
$this->connection = $connection;
}
public function loadUserByUsername($username)
{
return $this->getUser($username);
}
private function getUser($username)
{
$sql = "SELECT * FROM sf_users WHERE username = :name";
$stmt = $this->connection->prepare($sql);
$stmt->bindValue("name", $username);
$stmt->execute();
$row = $stmt->fetch();
if (!$row['username'])
{
$exception = new UsernameNotFoundException(sprintf('Username "%s" not found in the database.', $row['username']));
$exception->setUsername($username);
throw $exception;
}
else
{
return new User($row['username'], $row['password'], $row['roles']);
}
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof User)
{
throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
}
return $this->getUser($user->getUsername());
}
public function supportsClass($class)
{
return 'Sfauth\User\User' === $class;
}
}
The user provider must implement the UserProviderInterface interface. We are using the doctrine DBAL to perform the database-related operations. As we have implemented the UserProviderInterface interface, we must implement the loadUserByUsername , refreshUser , and supportsClass methods.
The loadUserByUsername method should load the user by the username, and that's done in the getUser method. If the user is found, we return the corresponding Sfauth\User\User object, which implements the UserInterface interface.
On the other hand, the refreshUser method refreshes the supplied User object by fetching the latest information from the database.
And finally, the supportsClass method checks if the DatabaseUserProvider provider supports the supplied user class.
The Database Authentication Provide