本文概述
从Symfony 2开始, FOSUserBundle无疑是实现数据库支持的用户系统的最常用的捆绑软件。它的安装非常容易且易于理解, 但是作为一个喜欢从头开始实现很多东西的开发人员(因此我以后可以自定义一些行为), 捆绑中的某些东西对我来说并不是正确的选择。随着Symfony 4的引入, FOSUserBundle并没有提供很好的兼容性, 这意味着当你在应用程序上启用捆绑软件时, 你会发现很多弃用警告。
大多数开发人员只需要实现基于基本数据库的用户身份验证系统, 这包括创建用户表, 注册和登录表单。在本系列中, 我们将逐步向你介绍如何在Symfony 4.3中自行创建此用户系统。
1.创建用户实体
像在每个需要身份验证的应用程序上一样, 它将需要一个User实体, 该实体将在数据库中的表中至少包含4个字段, 即user:
- id(可自动递增)
- 电子邮件
- 角色
- 密码
默认情况下, Symfony 4提供了一种非常简单的方法, 可通过php bin / console make:user命令在应用程序中生成此User实体。但是, 我们喜欢手动制作一些东西, 因此我们可以了解我们在做什么, 它如何工作以及为什么有必要。创建用户实体的第一步是在项目的src / Entity目录中创建User.php类。它基本上将包含数据库上用户表的表结构。在我们的例子中, 我们将有一个包含以下6个字段的表格:
- id(可自动递增)
- 电子邮件
- 角色
- 密码
- 名字
- 姓
这种方法的主要优点是你可以轻松地向用户表添加新字段而不会出现问题:
<?php
// src/Entity/User.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @var string|null
*
* @ORM\Column(name="first_name", type="string", length=255, nullable=true)
*/
private $firstName;
/**
* @var string|null
*
* @ORM\Column(name="last_name", type="string", length=255, nullable=true)
*/
private $lastName;
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
public function getId(): ?int
{
return $this->id;
}
public function getFirstName(): ?string
{
return $this->firstName;
}
public function setFirstName(?string $name): self
{
$this->firstName = $name;
return $this;
}
public function getLastName(): ?string
{
return $this->lastName;
}
public function setLastName(?string $name): self
{
$this->lastName = $name;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* @see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* @see UserInterface
*/
public function getSalt()
{
// not needed when using the "auto" algorithm in security.yaml
}
/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
创建User实体后, 你还将注意到在类的注释中我们定义了一个Repository类, 因此你也需要创建它(也可以删除@ORM \ Entity(repositoryClass =” App \ Repository \ UserRepository”)注释)。 UserRepository.php类包含以下代码, 它将位于src / Repository目录中:
<?php
// src/Repository/UserRepository.php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Persistence\ManagerRegistry;
/**
* @method User|null find($id, $lockMode = null, $lockVersion = null)
* @method User|null findOneBy(array $criteria, array $orderBy = null)
* @method User[] findAll()
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
// /**
// * @return User[] Returns an array of User objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->orderBy('u.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?User
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
在此类中, 你可以为存储库定义自定义方法, 例如findByEmailProvider, findByAge或某些自定义方法。创建用户实体后, 请确保在.env文件上具有已定义的数据库连接:
# app/.env
DATABASE_URL=mysql://user:password@127.0.0.1:3306/database_name
并运行以下命令以更新数据库结构:
php bin/console doctrine:schema:update --force
这将在我们的数据库中创建用户表:
现在我们有了用户实体和数据库上的表, 我们可以继续配置security.yaml文件。
2.配置security.yaml
已经使用最基本的字段定义和配置了用户实体, 现在我们需要向框架指定将使用此类实体作为应用程序的用户提供程序:
# app/config/packages/security.yaml
security:
encoders:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
如Symfony官方文档中所指定, 在此版本的Symfony中, 你需要将密码的哈希算法设置为auto。如果你手动定义bcrypt, argon2i或钠, 你将在项目中看到过时警告, 这正是我们希望通过实施自己的身份验证系统避免的警告。发生这种情况的原因是散列器的发展快节奏, 因此越来越不建议选择特定的散列算法, 这就是NativePasswordEncoder发挥作用的时候。
3.创建测试用户
现在, 为了完成本教程的这一部分, 我们将需要在数据库上至少创建一个用户, 以便稍后可以使用本教程的其余部分。到目前为止, 我们还没有实现用于注册或登录的任何形式, 但是我们实现了使系统与User实体一起工作的后端逻辑。根据我们的数据库, 我们只需要简单地用电子邮件, 姓氏和密码注册用户即可。在此步骤中, 我们唯一的问题是如何使用有效的密码注册用户, 因为如你现在所了解的那样, 出于明显的原因, 我们不会在数据库中存储纯文本密码。
由于我们不在本教程的本部分中实现表单, 因此我们可以轻松地使用sql查询在数据库中手动注册用户。但是, 现在我们应该从哪里获得密码的哈希版本?我们可以通过带有代码的UserPasswordEncoderInterface或在cli上使用security:encode-password命令来执行此操作。我们将通过两种方式向你说明如何创建用户:
A.使用SQL查询手动创建用户
第一种选择是简单地使用PHPMyAdmin之类的工具或相关工具通过SQL查询注册用户。我们这样做是为了让你可以轻松了解当前发生的情况。我们将需要使用NativePasswordEncoder对用户密码进行哈希处理, 因此, 如果你不想编写PHP代码来注册该用户, 则只需使用以下命令对cli中的密码进行哈希处理即可, 你可以在其中提供位置参数将分配给用户的纯文本密码, 在这种情况下, 它将是” MyTestPassword”:
php bin/console security:encode-password MyTestPassword
在终端中运行上一个命令将生成以下输出:
如你所见, 使用Symfony的本机密码编码器的编码密码” MyTestPassword”为$ argon2id $ v = 19 $ m = 65536, t = 4, p = 1 $ cXczUUhnTWJwRC4uQkNDSA $ PswUIjNtoOwGVwGj / e8DamrwQZKMqhzr4N390。有了密码后, 我们可以使用以下查询在数据库中轻松创建用户(请注意, 需要使用你自己的计算机上的命令生成的编码密码来更改密码):
INSERT INTO `user` (
`id`, `email`, `roles`, `password`, `first_name`, `last_name`
) VALUES (
NULL, 'dev@ourcodeworld.com', '[]', '$argon2id$v=19$m=65536, t=4, p=1$aU1QY2pLSjRjdzUwM2QyLw$8rwHEb9OxHusAt5yfSjHf3CtIkeWTFJxBy8IFDYa2jQ', 'Carlos', 'Delgado'
);
B.使用控制器内部的Doctrine手动创建用户
或者, 如果要使用PHP代码创建测试用户, 则可以使用以下代码在测试控制器中快速编写自定义操作, 该操作将在数据库中持久存储新的User实体:
<?php
// src/Controller/TestController.php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
// Import the User entity
use App\Entity\User;
// Import the Password Encoder Interface
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class TestController extends AbstractController
{
/**
* @Route("/register-test-user", name="app_register_testuser")
*/
public function register(UserPasswordEncoderInterface $passwordEncoder)
{
// Retrieve the entity manager
$entityManager = $this->getDoctrine()->getManager();
// Create a new user with random data
$user = new User();
$user
->setEmail("dev@ourcodeworld.com")
->setPassword($passwordEncoder->encodePassword(
$user, 'MyTestPassword'
))
->setFirstName("Carlos")
->setLastName("Delgado");
$entityManager->persist($user);
$entityManager->flush();
return new Response(
'<html><body>Test User Registered Succesfully</body></html>'
);
}
}
按照创建测试用户的任何前述步骤, 它将在数据库中存储一条记录, 其中包含以下数据:
这就是这部分的全部。如你所见, 我们实现了身份验证系统的最基本方面, 即基本上是用户实体的实现。
阅读教程”如何在Symfony 4.3中实现自己的用户身份验证系统”的所有部分。
- 第1部分:创建自定义用户类。
- 第2部分:创建用户注册表单。
- 第3部分:创建登录表单和注销路径。
本教程感兴趣的链接
- https://symfony.com/doc/current/security.html#a-create-your-user-class
- https://symfony.com/doc/current/doctrine/registration_form.html
- https://symfony.com/doc/current/security.html#create-user-class
- https://symfony.com/doc/current/security.html#a-create-your-user-class
- https://stackoverflow.com/questions/6832445/how-can-bcrypt-have-built-in-salts
- https://stackoverflow.com/questions/23629571/create-tables-with-entities-doctrine-symfony2
编码愉快!
评论前必须登录!
注册