繁体   English   中英

如何在Symfony 4中制作自定义身份验证提供程序?

[英]How to make a custom Authentication Provider in Symfony 4?

我想创建一个自定义的身份验证提供程序,但我真的不知道如何开始,问题是我们只想将用户的电子邮件存储在数据库中,而没有其他方法。

每个月,密码都会更改,每个用户都会使用该密码来允许他们访问该网站。 登录表单如下所示:

  • 电子邮件字段,用于将电子邮件存储在数据库中
  • 一个密码字段,但不与用户一起存储,它只是每个人的一个密码。

我不知道是否必须为数据库中或数据库中的每个用户存储相同的密码...我希望通过symfony将用户标识为“已登录”,我知道我必须创建一个自定义身份验证提供程序,但我不知道不知道如何...谢谢您的帮助。

因此,对于那些正在寻找此问题的解决方案的人,我这样解决了它:我制作了一个自定义UserProvider。 这用于在数据库中创建用户(如果该用户不存在)。 (查看loadUserByUsername函数)

<?php

namespace App\Security\User;

use App\Entity\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 App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;

class CustomProvider implements UserProviderInterface
{
    private $repository;
    private $em;

    public function __construct(UserRepository $repository, EntityManagerInterface $em)
    {
        $this->repository = $repository;
        $this->em = $em;
    }

    public function loadUserByUsername($username)
    {
        $userData = $this->repository->findOneByEmail($username);
        $password = 'secure_p@ssw0rd';
        $user = new User($username, $password);

        if (!$userData) {
            $user = new User($username, $password);
            $this->em->persist($user);
            $this->em->flush();
        }
        return $user;
    }

    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof User) {
            throw new UnsupportedUserException(
                sprintf('Instances of "%s" are not supported.', get_class($user))
            );
        }

        return $this->loadUserByUsername($user->getUsername());
    }

    public function supportsClass($class)
    {
        return User::class === $class;
    }
}

然后,我创建了一个自定义的User类(我只是将电子邮件存储在数据库中,没有别的):

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * @ORM\Table(name="leclan_users")
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User implements UserInterface, \Serializable
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255, unique=true)
     * @Assert\NotBlank()
     * @Assert\Email()
     */
    private $email;

    private $password;

    private $roles = [];

    public function __construct($email, $password, array $roles = array())
    {
        $this->email = $email;
        $this->password = $password;
        $this->roles = $roles;
    }

    public function getId(): int
    {
        return $this->id;
    }

    public function getUsername(): ?string
    {
        return $this->email;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): void
    {
        $this->email = $email;
    }

    public function getPassword(): ?string
    {
        return 'secure_p@ssw0rd';
    }

    public function setPassword(string $password): void
    {
        $this->password = $password;
    }

    public function getRoles(): array
    {
        $roles = $this->roles;
        if (empty($roles)) {
            $roles[] = 'ROLE_USER';
        }
        return array_unique($roles);
    }

    public function setRoles(array $roles): void
    {
        $this->roles = $roles;
    }

    public function getSalt(): ?string
    {
        return null;
    }

    public function eraseCredentials(): void
    {
    }

    public function serialize(): string
    {
        return serialize([$this->id, $this->email]);
    }

    public function unserialize($serialized): void
    {
        [$this->id, $this->email] = unserialize($serialized, ['allowed_classes' => false]);
    }
}

然后,对于密码调整,我使用了自定义的PasswordEncoder:

<?php

namespace App\Security\Encoder;

use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;

class CustomEncoder extends BasePasswordEncoder
{
    public function encodePassword($raw, $salt)
    {
        return '';
    }

    public function isPasswordValid($encoded, $raw, $salt)
    {
        if ($this->isPasswordTooLong($raw)) {
            return false;
        }

        if ($raw === 'secure_p@ssw0rd') {
            return true;
        }
    }
}

这个方法可以正常工作,但是我不确定encodePassword方法(如果我只返回一个空字符串就可以了吗?我根本没有使用该方法。)

这也是我的security.yaml文件:

security:
    encoders:
        App\Entity\User:
            id: App\Security\Encoder\CustomEncoder

    providers:
        custom:
            id: App\Security\User\CustomProvider

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            provider: custom
            form_login:
                login_path: security_login
                default_target_path: app_main
                check_path: security_login
            logout:
                path: security_logout
                target: security_login

    access_control:
        - { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: IS_AUTHENTICATED_FULLY }

感谢@Cerad谁给了我这个解决方案

看来您有一个非常自定义的身份验证过程。 为此,我建议使用GuardAuthenticator 您要解决的第一个问题有些复杂,因此,让我按照您的理解,逐步完成身份验证过程。

  1. 支持

    由于您始终想使用此身份验证系统,因此它应该始终返回true。

  2. getCredentials

    这是我要检查的两件事。 我们正在尝试登录,所以我将检查请求是否在登录路径和POST方法上,或者是否设置了标识先前编写用户的会话密钥。 如果都不为真,则只返回null并跳过其余的身份验证。

  3. getUser

    我们要么从会话中读取用户(如果他们先前已登录),要么在提交的请求数据中寻找用户名。

  4. checkCredentials

    之前登录时,我们可以返回true。 如果我们处于身份验证的中间(对登录路径的POST请求),则只需检查主密码表中提供的密码是否与我们当前的主密码匹配即可。

    注意:例如,您仍然可以使用Symfony的PasswordEncoders(例如bcrypt),但这可能会更有效。 随时提出后续问题。

  5. onAuthenticationSuccess

    我们将用户数据保存到会话中。

  6. onAuthenticationFailure

    在这里,我将实现您的第二个方案,在该方案中,您将根据提供的输入创建一个新用户。

如果需要更多的详细信息,请随意提问。

编辑澄清:我写了存储/从会话中获取一个值。 为此,您应该使用Symfony的TokenStorage。 这将使用户可以像普通登录一样使用。 如果您将其手动存储在会话中,则可能需要进行一些调整,以使其在身份验证后可以正常播放。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM