Пароль не хешируется, и при попытке подключения возвращается ошибка 500 Symfony - PullRequest
0 голосов
/ 04 мая 2018

Я довольно новичок в Symfony в целом, я в основном использовал его, потому что мне нужно было сделать что-то безопасное очень быстро, а также открыть Symfony 4.

Я пытаюсь установить безопасную связь с рецептом безопасности, но сталкиваюсь с двумя основными проблемами (вероятно, связанными) и небольшой.

Сначала я попытался определить соль как nullable, но она по-прежнему NOT NULL в дБ. Вот мое определение столбца:

/**
 * @ORM\Column(name="salt", type="string", nullable=true)
 */
private $salt;

Так что теперь большие проблемы: пароли, которые я добавляю, не хэшируются, и попытка подключения возвращает ошибку 500

Я пытался следовать документации и вот: Моя сущность

use Doctrine\ORM\Mapping as ORM;
use PhpParser\Node\Scalar\String_;
use Symfony\Component\Security\Core\User\UserInterface;

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

    /**
     * @ORM\Column(type="string", length=25, unique=true)
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $password;

    /**
     * @ORM\Column(type="string", length=254, unique=true, nullable=true)
     */
    private $email;

    /**
     * @ORM\Column(name="is_active", type="boolean")
     */
    private $isActive;

    /**
     * @ORM\Column(name="salt", type="string", nullable=true)
     */
    private $salt;

    /**
     * @ORM\Column(name="alias", type="string")
     */
    private $alias;

    /**
     * @return mixed
     */
    public function getAlias()
    {
        return $this->alias;
    }

    /**
     * @param mixed $alias
     */
    public function setAlias($alias): void
    {
        $this->alias = $alias;
    }

    public function __construct()
    {
        $this->isActive = true;
        // may not be needed, see section on salt below
//        $this->salt = md5(uniqid('', true));
    }

    public function getUsername()
    {
        return $this->username;
    }

    public function getSalt() :String
    {
        // you *may* need a real salt depending on your encoder
        // see section on salt below
        return $this->salt;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function getRoles()
    {
        return array('ROLE_USER');
    }

    public function eraseCredentials()
    {
    }

    /** @see \Serializable::serialize() */
    public function serialize()
    {
        return serialize([
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
//            $this->salt
        ]);
    }

    /** @see \Serializable::unserialize() */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
//            $this->salt
            ) = unserialize($serialized, ['allowed_classes' => false]);
    }

    /**
     * @return mixed
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @param mixed $id
     */
    public function setId($id): void
    {
        $this->id = $id;
    }

    /**
     * @return mixed
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * @param mixed $email
     */
    public function setEmail($email): void
    {
        $this->email = $email;
    }

    /**
     * @return mixed
     */
    public function getisActive()
    {
        return $this->isActive;
    }

    /**
     * @param mixed $isActive
     */
    public function setIsActive($isActive): void
    {
        $this->isActive = $isActive;
    }

    /**
     * @param mixed $username
     */
    public function setUsername($username): void
    {
        $this->username = $username;
    }

    /**
     * @param mixed $password
     */
    public function setPassword($password): void
    {
        $this->password = $password;
    }

    /**
     * @param mixed $salt
     */
    public function setSalt($salt): void
    {
        $this->salt = $salt;
    }
}

Мои контроллеры

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;

class SecurityController extends Controller
{
    /**
     * @Route("/login", name="login")
     */
    public function login(Request $request, AuthenticationUtils $authenticationUtils)
    {
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();

        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render('security/login.html.twig', array(
            'last_username' => $lastUsername,
            'error'         => $error,
        ));
    }
}

и

use App\Entity\User;
use App\Form\UserType;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;


/**
 * @Route("/user")
 */
class UserController extends Controller
{
    /**
     * @Route("/", name="user_index", methods="GET")
     */
    public function index(UserRepository $userRepository): Response
    {
        return $this->render('user/index.html.twig', ['users' => $userRepository->findAll()]);
    }

    /**
     * @Route("/new", name="user_new", methods="GET|POST")
     */
    public function new(Request $request): Response
    {
        $user = new User();
        $form = $this->createForm(UserType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($user);
            $em->flush();

            return $this->redirectToRoute('user_index');
        }

        return $this->render('user/new.html.twig', [
            'user' => $user,
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/{id}", name="user_show", methods="GET")
     */
    public function show(User $user): Response
    {
        return $this->render('user/show.html.twig', ['user' => $user]);
    }

    /**
     * @Route("/{id}/edit", name="user_edit", methods="GET|POST")
     */
    public function edit(Request $request, User $user): Response
    {
        $form = $this->createForm(UserType::class, $user);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $this->getDoctrine()->getManager()->flush();

            return $this->redirectToRoute('user_edit', ['id' => $user->getId()]);
        }

        return $this->render('user/edit.html.twig', [
            'user' => $user,
            'form' => $form->createView(),
        ]);
    }

    /**
     * @Route("/{id}", name="user_delete", methods="DELETE")
     */
    public function delete(Request $request, User $user): Response
    {
        if ($this->isCsrfTokenValid('delete'.$user->getId(), $request->request->get('_token'))) {
            $em = $this->getDoctrine()->getManager();
            $em->remove($user);
            $em->flush();
        }

        return $this->redirectToRoute('user_index');
    }

    public function register(User $user, UserPasswordEncoderInterface $encoder)
    {
        $plainPassword = $user->getPassword();
        $encoded = $encoder->encodePassword($user, $plainPassword);
        $user->setPassword($encoded);
    }
}

и мой security.yaml

security:
    # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
    providers:
        db_provider:
            entity:
                class: App\Entity\User
                property: username

    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~
            provider: db_provider
            form_login:
                login_path: login
                check_path: login
            logout:
                path:   /logout
                target: /homepage
            pattern:    ^/admin
            http_basic: ~

    encoders:
        App\Entity\User:
            algorithm: argon2i

    # Easy way to control access for large sections of your site
    # Note: Only the *first* access control that matches will be used
    access_control:
        - { path: ^/admin, roles: ROLE_ADMIN }

Я попытался добавить это после проверки, если для isSubmited и isValid в моем UserController :: new ()

$plainPassword = $user->getPassword;
$encoded = $encoder->encodePassword($user, $plainPassword);
$user->setPassword($encoded);

Но у меня возникла ошибка, сказав, что UserPasswordEncoderInterface $encoder, который я передал в качестве аргумента метода, не вводился при загрузке формы. Тем не менее, я не уверен, что было бы хорошим решением заставить его работать, поскольку мне пришлось бы дублировать эту логику в UserController :: edit (), который не похож на код, подобный Symfony.

(ошибка:)

"Controller" App \ Controller \ UserController :: new () "требует, чтобы вы указали значение для аргумента" $ encoder ". Либо аргумент имеет значение NULL, и значение NULL не предоставлено, значение по умолчанию не предоставлено или потому что после этого есть необязательный аргумент. "

Я также пытался скопировать / вставить (как я отчаялся ...) код в моем UserController, а затем в SecurityController, но это тоже не сработало

public function register(UserPasswordEncoderInterface $encoder)
{
    // whatever *your* User object is
    $user = new App\Entity\User();
    $plainPassword = 'ryanpass';
    $encoded = $encoder->encodePassword($user, $plainPassword);

    $user->setPassword($encoded);
}

Я получаю это как журнал с сервера:

"Для учетной записи" App \ Entity \ User "не настроен кодировщик."

Я также пытался вставить непосредственно в мою базу данных некоторые значения, но при попытке подключения я получил сообщение «Отказано в доступе» при вводе правильного пароля, что, я думаю, является еще одной проблемой ...

Я действительно не понимаю, где я неправ, и я не мог найти людей, спрашивающих об этом. Буду искренне признателен, если вы поможете мне.

Примечание: Маршруты UserController начинаются с / user и являются полностью общедоступными, поскольку мне нужен пользователь для доступа к защищенной панели администратора.

EDIT Я использую MySQL 5.7 и PHP 7.2, если это может быть связано

Ответы [ 2 ]

0 голосов
/ 04 мая 2018

Я наконец нашел решение благодаря @LeonWillens. На самом деле удаление свойства соли и сеттеров заставило меня обнаружить, что рецепт безопасности идет без валидатора. Итак, я побежал composer require doctrine form security validator. Я добавил в текстовое поле поле обычного текста, которое не является столбцом

/**
 * @Assert\NotBlank()
 * @Assert\Length(max=4096)
 */
private $plainPassword;

С этим я мог бы добавить эту логику в UserController :: new ()

/**
 * @Route("/new", name="user_new", methods="GET|POST")
 */
public function new(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
{
    $user = new User();
    $form = $this->createForm(UserType::class, $user);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
        $user->setPassword($password);
        $em = $this->getDoctrine()->getManager();
        $em->persist($user);
        $em->flush();

        return $this->redirectToRoute('user_index');
    }

    return $this->render('user/new.html.twig', [
        'user' => $user,
        'form' => $form->createView(),
    ]);
}

Я меняю кодировщики в моем security.yaml

encoders:
    Symfony\Component\Security\Core\User\User: plaintext
    App\Entity\User:
        algorithm: argon2i

А теперь добавление пользователя работает отлично. У меня все еще есть проблемы со связью, но нет такой вещи как выброшенное исключение

0 голосов
/ 04 мая 2018

Поскольку вы используете Argon2i в качестве алгоритма кодирования для вашей сущности, ваш $salt устареет:

Вам нужно использовать свойство Salt?

Если вы используете bcrypt или argon2i, нет. Иначе да. Все пароли должны быть хешированы с помощью соли, но bcrypt и argon2i делают это внутренне [...], метод getSalt () в User может просто вернуть null (он не используется). [...]

- Как загрузить пользователей Security из базы данных (поставщик сущностей)

Попробуйте удалить свойство $salt и метод установки, и пусть ваш getSalt() вернет null. Сохраните пользователя без операций кодирования и проверьте сохраненный пароль.

Хотя это можно считать грязным хаком, это хорошая практика ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...