首页 > 解决方案 > 密码没有得到散列,并尝试连接返回错误 500 Symfony

问题描述

总的来说,我对 Symfony 还是很陌生,我主要使用它是因为我需要非常快速地做一些安全的事情,并且还需要发现 Symfony 4。

我正在尝试与安全配方建立安全连接,但我面临两个主要问题(可能相关)和一个小问题。

首先,我尝试将 salt 定义为可为空,但它仍在NOT NULLdb 中。这是我对列的定义:

/**
 * @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 }

在我的 UserController::new() 检查 isSubmited 和 isValid 后,我尝试添加它

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

但是我有一个错误说UserPasswordEncoderInterface $encoder加载表单时没有注入我作为方法传递的参数。我仍然不确定让它工作是否是一个好的解决方案,因为我必须在 UserController::edit() 中复制该逻辑,这看起来不像 Symfony 的代码。

(错误 :)

"Controller "App\Controller\UserController::new()" 要求您为 "$encoder" 参数提供一个值。参数可以为空且未提供空值,未提供默认值,或者因为存在在此之后是一个非可选参数。”

我还尝试复制/粘贴(我多么绝望……)我的 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 开头并且是完全公开的,因为我需要一个用户来访问安全的管理面板。

编辑我正在使用 MySQL 5.7 和 PHP 7.2,如果可以的话

标签: phpsymfonysymfony4

解决方案


由于您使用 Argon2i 作为实体的编码器算法,因此您$salt已过时:

您需要使用 Salt 属性吗?

如果您使用 bcrypt 或 argon2i,则不会。否则,是的。所有密码都必须使用盐进行哈希处理,但 bcrypt 和 argon2i 在内部执行此操作 [...] User 中的 getSalt() 方法只能返回 null (未使用)。[...]

-如何从数据库中加载安全用户(实体提供者)

尝试删除$salt属性和 setter 方法,然后让您的getSalt()return null. 持久化用户而不进行编码操作并检查持久化密码。

虽然这可以被视为一种肮脏的黑客行为,但这似乎是一种很好的做法......


推荐阅读