добавить меня в Symfony 4.2 Security с помощью пользовательского объекта - PullRequest
0 голосов
/ 08 апреля 2019

Я работаю с Symfony 4.2,

Я работаю с компонентом безопасности и пытаюсь добавить запомнить меня.

Сначала «Помни меня» работало для меня, но когдаМой пользовательский объект стал настроенным, помните, что я больше не работает,

Мой пользовательский пользователь:

Я подключен к базе данных WordPress, что я не могу внести в нее изменения (Я должен только для чтения), и мне нужно добавить какое-то поле для пользователя, поэтому я должен создать новую таблицу User OneToOne с WpUsers (пользователи WordPress),

Итак, я использую доктрину для создания сущности изсуществующую БД, я не затрагивал эти сущности, я просто создал свой пользовательский объект только для того, чтобы добавить поле ролей в пользовательскую систему:

Пропуск Wordpress хэшируется с помощью phpass.

Entity \ WpUsers(генерируется доктриной):

/**
 * WpUsers
 *
 * @ORM\Table(name="wp_users", indexes={@ORM\Index(name="user_nicename", columns={"user_nicename"}), @ORM\Index(name="user_login_key", columns={"user_login"}), @ORM\Index(name="user_email", columns={"user_email"})})
 * @ORM\Entity(repositoryClass="App\Repository\WpUsersRepository")
 */
class WpUsers
{
    /**
     * @var int
     *
     * @ORM\Column(name="ID", type="bigint", nullable=false, options={"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="user_login", type="string", length=60, nullable=false)
     */
    private $userLogin = '';

    /**
     * @var string
     *
     * @ORM\Column(name="user_pass", type="string", length=255, nullable=false)
     */
    private $userPass = '';

    /**
     * @var string
     *
     * @ORM\Column(name="user_nicename", type="string", length=50, nullable=false)
     */
    private $userNicename = '';

    /**
     * @var string
     *
     * @ORM\Column(name="user_email", type="string", length=100, nullable=false)
     */
    private $userEmail = '';

    /**
     * @var string
     *
     * @ORM\Column(name="user_url", type="string", length=100, nullable=false)
     */
    private $userUrl = '';

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="user_registered", type="datetime", nullable=false, options={"default"="0000-00-00 00:00:00"})
     */
    private $userRegistered = '0000-00-00 00:00:00';

    /**
     * @var string
     *
     * @ORM\Column(name="user_activation_key", type="string", length=255, nullable=false)
     */
    private $userActivationKey = '';

    /**
     * @var int
     *
     * @ORM\Column(name="user_status", type="integer", nullable=false)
     */
    private $userStatus = '0';

    /**
     * @var string
     *
     * @ORM\Column(name="display_name", type="string", length=250, nullable=false)
     */
    private $displayName = '';

Entity \ User.php:

<?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;

    /**
     * @ORM\Column(type="json_array")
     */
    private $roles = [];

    /**
     * @ORM\OneToOne(targetEntity="App\Entity\WpUsers", cascade={"persist", "remove"})
     * @ORM\JoinColumn(name="wp_user_id", referencedColumnName="ID",nullable=false)
     */
    private $wpUser;

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

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUsername(): string
    {
        return $this->getWpUser()->getUserLogin();
    }

    /**
     * @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()
    {
        return $this->getWpUser()->getUserPass();
    }

    /**
     * @see UserInterface
     */
    public function getSalt()
    {
        // not needed for apps that do not check user passwords
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials()
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }

    public function getWpUser(): ?WpUsers
    {
        return $this->wpUser;
    }

    public function setWpUser(WpUsers $wpUser): self
    {
        $this->wpUser = $wpUser;

        return $this;
    }

}

security.yaml:

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    # encoders:
    #         App\Entity\WpUsers:
    #             algorithm: bcrypt
    providers:
        # in_memory: { memory: ~ }
        app_user_provider:
            entity:
                class: App\Entity\User
                property: wpUser.userLogin 
firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            guard:
                authenticators:
                    - App\Security\LoginFormAuthenticator

            remember_me:
                secret:   '%kernel.secret%'
            lifetime: 604800 # 1 week in seconds

Безопасность \ LoginFormAuthenticator:

namespace App\Security;
// use ...
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    // private $passwordEncorder;
    private $router;

    public function __construct(
            EntityManagerInterface $entityManager, 
            UrlGeneratorInterface $urlGenerator, 
            CsrfTokenManagerInterface $csrfTokenManager,
            // UserPasswordEncoderInterface $passwordEncorder,
            // PasswordHash $passwordHash
            RouterInterface $router
            )
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        // $this->passwordEncorder = $passwordEncorder;
        $this->router = $router;
        $this->passwordHash = new PasswordHash(8,false);
    }

    public function supports(Request $request)
    {
        return 'app_login' === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'userLogin' => $request->request->get('userLogin'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['userLogin']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $wpUser = $this->entityManager->getRepository(WpUsers::class)->findOneBy(['userLogin' => $credentials['userLogin']]);

        if (!$wpUser) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('User Login could not be found.');
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['wpUser' => $wpUser ]);

        if(!$user){
            $user = new USER();
            $user->setWpUser($wpUser);
            $this->entityManager->persist($user);
            $this->entityManager->flush();
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {

        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        // return $this->passwordEncorder->isPasswordValid($user, $credentials['password']);  
        return $this->passwordHash->CheckPassword($credentials['password'],$user->getPassword());  

        // Check the user's password or other credentials and return true or false
        // If there are no credentials to check, you can just return true
        throw new \Exception('TODO: check the credentials inside '.__FILE__);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }

        // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
        // throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
        return new RedirectResponse($this->router->generate('commandes'));
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }


}

login.twig.html:

{% extends 'myBase.html.twig' %}

{% block title %}Log in!{% endblock %}

{% block body %}
<form method="post">
    {% if error %}
        <div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
    {% endif %}

    <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
    <label for="inputUserLogin" class="sr-only">User Login</label>
    <input type="text" value="{{ last_username }}" name="userLogin" id="inputUserLogin" class="form-control" placeholder="User Login" required autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>

    <input type="hidden" name="_csrf_token"
           value="{{ csrf_token('authenticate') }}"
    >


<!--     Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
    See https://symfony.com/doc/current/security/remember_me.html -->

    <div class="checkbox mb-3">
        <label>
            <input type="checkbox" name="_remember_me"> Remember me
        </label>
    </div>


    <button class="btn btn-lg btn-primary" type="submit">
        Sign in
    </button>
</form>
{% endblock %}

Ответы [ 2 ]

0 голосов
/ 09 апреля 2019

Ответ заключается в том, что мне нужно реализовать пользовательский поставщик, потому что мой пользовательский процесс загрузки не связан с прямой сущностью.

bin / console make: user

И выберите, что пользователь не должен быть сохранен в БД, чтобы CLI создал для вас UserProvider.

0 голосов
/ 08 апреля 2019

Я полагаю, что вам не хватает supportsRememberMe() Guard Authenticator метода. Как вы можете прочитать в документации :

supportsRememberMe()
    If you want to support "remember me" functionality, return true from this method. You will still 
    need to activate remember_me under your firewall for it to work...

Таким образом, решение должно добавить вышеупомянутый метод к вашему аутентификатору:

public function supportsRememberMe()
{
    return true;
}
...