簡體   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