我为什么要这么做?
here
…这也会带来副作用:
您将在开发人员倾向于使用的会话中使用此实体
在会话中,您将遇到同步问题。如果你
不是数据库里的。为了解决这个问题,你可以
每个请求都将实体合并回实体管理器。
虽然这解决了其中一个问题,但另一个常见问题是
其他物体,这会带来一些副作用:
(标准设置),它将尝试序列化包含
一种联系。这将喷出一些错误在您的屏幕上作为
无法序列化连接。哦,别想了
不完整对象的非序列化问题,因为缺少
属性。每个经过身份验证的用户都会触发此情况。
基本上,问题是如果您使用同一个实体进行身份验证和处理用户/员工/客户机等,您将遇到这样的问题:当您更改实体的属性时,它将导致已验证的用户与数据库中的内容不同步-导致角色不正确的问题,用户突然被迫注销(由于
logout_on_user_change setting
),或其他问题,具体取决于用户类在系统中的使用方式。
我假设您有一个“User”实体,它至少有用户名、密码和角色
为了解决这个问题,我们需要创建两个独立的服务,作为用户实体和身份验证用户之间的桥梁。
第一个是创建一个安全用户,它使用user类中的字段
/app/Security/SecurityUser.php
<?php
namespace App\Security;
use App\Entity\User;
use Symfony\Component\Security\Core\User\UserInterface;
class SecurityUser implements UserInterface, \Serializable
{
private $username;
private $password;
private $roles;
public function __construct(User $user)
{
$this->username = $user->getUsername();
$this->password = $user->getPassword();
$this->roles = $user->getRoles();
}
public function getUsername(): ?string
{
return $this->username;
}
public function getPassword(): ?string
{
return $this->password;
}
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
/** @see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->username,
$this->password,
// Should only be set if your encoder uses a salt i.e. PBKDF2
// This example uses Argon2i
// $this->salt,
));
}
/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->username,
$this->password,
// Should only be set if your encoder uses a salt i.e. PBKDF2
// This example uses Argon2i
// $this->salt
) = unserialize($serialized, array('allowed_classes' => false));
}
public function getRoles()
{
return $this->roles;
}
public function eraseCredentials()
{
}
}
安全用户提供程序
/应用程序/安全/安全用户提供程序
<?php
namespace App\Security;
use App\Repository\UserRepository;
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;
class SecurityUserProvider implements UserProviderInterface
{
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function loadUserByUsername($username)
{
return $this->fetchUser($username);
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof SecurityUser) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
$username = $user->getUsername();
$this->logger->info('Username (Refresh): '.$username);
return $this->fetchUser($username);
}
public function supportsClass($class)
{
return SecurityUser::class === $class;
}
private function fetchUser($username)
{
if (null === ($user = $this->userRepository->findOneBy(['username' => $username]))) {
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
return new SecurityUser($user);
}
}
这个服务基本上会要求查询数据库中的用户名,然后查询相关用户名的角色。如果找不到用户名,则会创建一个错误。然后将SecurityUser对象返回给Symfony进行身份验证。
现在我们需要告诉Symfony使用这个对象
/app/config/packages/security.yaml文件
security:
...
providers:
db_provider:
id: App\Security\SecurityUserProvider
“db\u provider”这个名字并不重要-你可以使用任何你想要的东西。此名称仅用于将提供程序映射到防火墙。如何配置防火墙超出了本文档的范围,请参阅
here
security:
...
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
provider: db_provider
form_login:
login_path: login
check_path: login
logout:
path: /logout
target: /
invalidate_session: true
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
最后,我们需要配置一个编码器,以便我们可以加密密码。
security:
...
encoders:
App\Security\SecurityUser:
algorithm: argon2i
memory_cost: 102400
time_cost: 3
threads: 4
注意,我使用的是Argon2i。内存开销、时间开销和线程的值是非常主观的,具体取决于您的系统。你可以看到我的帖子
here
它可以帮助您获得系统的正确值
在这一点上,你的安全性应该是工作的,你已经完全脱离了你的用户实体-恭喜!
here
.