【发布时间】:2018-10-14 10:41:10
【问题描述】:
总的来说,我对 Symfony 还是很陌生,我主要使用它是因为我需要非常快速地做一些安全的事情,并且还需要发现 Symfony 4。
我正在尝试与安全配方建立安全连接,但我面临两个主要问题(可能相关)和一个小问题。
首先,我尝试将 salt 定义为可为空,但它在 db 中仍然是 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 }
在我的 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" 参数提供一个值。参数可以为 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 开头并且是完全公开的,因为我需要一个用户来访问安全的管理面板。
编辑 如果可以的话,我正在使用 MySQL 5.7 和 PHP 7.2
【问题讨论】:
-
FOSUserBundle: github.com/FriendsOfSymfony/FOSUserBundle 呢? -
@MathieuDormeval,感谢您的回答,但至少目前看来这不是一个很好的解决方案。当我在他们的 Github 上阅读问题时,依赖这个 repo 不是一种安全的编码方式(但是?)
-
您的项目是否要求您将盐存储在数据库中?
-
@LeonWillens 根据文档,一点也不。但我必须有它来实现 UserInterface
-
@Zyigh 如果您的答案与接受的答案明显不同,您必须添加另一个答案(如果它包含更好的解决方案,则接受它)或编辑现有答案。您应该切勿在问题中添加答案,因此请记住删除该部分。