Подтвердить что ты не робот

Проверка подлинности вручную Symfony 2

Я работаю над приложением Symfony 2, где пользователь должен выбрать профиль во время процесса входа в систему.

У пользователей может быть многопрофильный профиль для работы, и они знают только свои собственные профили. Поэтому, во-первых, мне нужно запросить имя пользователя и пароль, если они верны, я не должен входить в систему пользователя, мне нужно указать, какой пользователь будет использовать пользователь во время сеанса.

Итак, я показываю форму с полем имени пользователя и пароля и отправляю ее с помощью запроса Ajax, этот запрос отвечает списком профилей, если имя пользователя и пароль верны или код ошибки в противном случае. Наконец, пользователь входит в систему, используя имя пользователя, пароль и профиль.

Проблема заключается в том, что я не знаю, как проверить правильность данных аутентификации (с использованием всех моих менеджеров аутентификации, поставщиков пользователей и т.д.), чтобы выполнить этот промежуточный шаг (подсказки для профиля), фактически не регистрируя пользователя.

Может ли кто-нибудь помочь мне с этим?

4b9b3361

Ответ 1

Я использовал код @Jordon и @Potor Polak, чтобы обернуть логику в автономной службе который использовал текущий токен доступа для проверки пароля. Возможно, некоторые нуждаются в этом:

services.yml:

app.validator.manual_password:
    class: AppBundle\Service\ManualPasswordValidator
    arguments:
        - '@security.token_storage'
        - '@security.encoder_factory'

ManualPasswordValidator.php:

<?php

namespace AppBundle\Service;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;

/**
 * Class ManualPasswordValidator
 *
 * @package AppBundle\Service
 */
class ManualPasswordValidator
{
    /**
     * @var EncoderFactory
     */
    protected $encoderFactory;

    /**
     * @var TokenStorage
     */
    protected $tokenStorage;

    /**
     * ManualPasswordValidator constructor.
     * 
     * @param EncoderFactory $encoderFactory
     * @param TokenStorage $tokenStorage
     */
    public function __construct(TokenStorage $tokenStorage, EncoderFactory $encoderFactory)
    {
        $this->encoderFactory = $encoderFactory;
        $this->tokenStorage = $tokenStorage;
    }

    /**
     * @param $password
     * @return bool
     */
    public function passwordIsValidForCurrentUser($password)
    {
        $token = $this->tokenStorage->getToken();

        if ($token) {
            $user = $token->getUser();

            if ($user) {
                $encoder = $this->encoderFactory->getEncoder($user);

                if ($encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
                    return true;
                }
            }
        }

        return false;
    }
}

После этого вы можете ввести ManualPasswordValidator туда, где хотите, и использовать его как:

$password        = $request->get('password');
$passwordIsValid = $this->manualPasswordValidator->passwordIsValidForCurrentUser($password);

Ответ 2

Проблема с кодом @Jordon заключается в том, что он не будет работать с алгоритмами хэширования, которые генерируют разные хэши для одного и того же пароля (например, bcrypt, который расскажет внутренне о своих параметрах, как о количестве итераций, так и о соли). Правильнее использовать isPasswordValid для Encoder для сравнения паролей.

Вот улучшенный код, который отлично работает с bcrypt:

$username = trim($this->getRequest()->query->get('username'));
$password = trim($this->getRequest()->query->get('password'));

$em = $this->get('doctrine')->getManager();
$query = $em->createQuery("SELECT u FROM \Some\Bundle\Entity\User u WHERE u.username = :username");
$query->setParameter('username', $username);
$user = $query->getOneOrNullResult();

if ($user) {
  // Get the encoder for the users password
  $encoder_service = $this->get('security.encoder_factory');
  $encoder = $encoder_service->getEncoder($user);

  // Note the difference
  if ($encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
    // Get profile list
  } else {
    // Password bad
  }
} else {
  // Username bad
}

Ответ 3

Вы можете сделать что-то подобное, чтобы получить пользователя и вручную протестировать пароль -

$username = trim($this->getRequest()->query->get('username'));
$password = trim($this->getRequest()->query->get('password'));

$em = $this->get('doctrine')->getEntityManager();
$query = $em->createQuery("SELECT u FROM \Some\Bundle\Entity\User u WHERE u.username = :username");
$query->setParameter('username', $username);
$user = $query->getOneOrNullResult();

if ($user) {
  // Get the encoder for the users password
  $encoder_service = $this->get('security.encoder_factory');
  $encoder = $encoder_service->getEncoder($user);
  $encoded_pass = $encoder->encodePassword($password, $user->getSalt());

  if ($user->getPassword() == $encoded_pass) {
    // Get profile list
  } else {
    // Password bad
  }
} else {
  // Username bad
}

Как только вы получите свой профиль от клиента, вы также можете легко выполнить логин вручную на контроллере сервера AJAX -

// Get the security firewall name, login
$providerKey = $this->container->getParameter('fos_user.firewall_name');
$token = new UsernamePasswordToken($user, $password, $providerKey, $user->getRoles());
$this->get("security.context")->setToken($token);

// Fire the login event
$event = new InteractiveLoginEvent($this->getRequest(), $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);

Возможно, потребуется несколько строк -

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;

Ответ 4

Единственный способ аутентификации моих пользователей на контроллере - это сделать подзапрос, а затем перенаправить. Вот мой код, я использую silex, но вы можете легко адаптировать его к symfony2:

$subRequest = Request::create($app['url_generator']->generate('login_check'), 'POST', array('_username' => $email, '_password' => $password, $request->cookies->all(), array(), $request->server->all());

$response = $app->handle($subRequest, HttpKernelInterface::MASTER_REQUEST, false);

return $app->redirect($app['url_generator']->generate('curriculos.editar'));

Ответ 5

В Symfony 4 в User Controller рекомендуется использовать UserPasswordEncoderInterface. Просто добавьте UserPasswordEncoderInterface в качестве параметра функции, в которой вы хотите проверить пароль, а затем добавьте приведенный ниже код.

public function changePasswordAction($old, $new, UserPasswordEncoderInterface $enc) {
   // Fetch logged in user object, can also be done differently.
   $auth_checker = $this->get('security.authorization_checker');
   $token = $this->get('security.token_storage')->getToken();
   $user = $token->getUser();

   // Check for valid password
   $valid = $encoder->isPasswordValid($user, $old);

   // Do something, e.g. change the Password
   if($valid)
      $user->setPassword($encoder->encodePassword($user, $new));
}