Мы создаем бизнес-приложение с нуля в Symfony 2, и я столкнулся с некоторой проблемой с потоком регистрации пользователей: после того, как пользователь создал учетную запись, они должны автоматически войти в систему с этими учетных данных, вместо того, чтобы сразу же принудительно предоставить свои учетные данные снова.
У кого-нибудь был опыт с этим, или он мог указать мне в правильном направлении?
Symfony 2.6.x - Symfony 3.0.x
Как и в случае с symfony 2.6 security.context
, не рекомендуется использовать security.token_storage
. Теперь контроллер может просто быть:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;
class LoginController extends Controller{
public function registerAction()
{
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('_security_main', serialize($token));
}
}
В то время как это устарело, вы все равно можете использовать security.context
, поскольку это было сделано для обратной совместимости. Просто будьте готовы обновить его для Symfony 3
Подробнее об изменениях 2.6 для безопасности вы можете прочитать здесь: https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md
Symfony 2.3.x
Чтобы выполнить это в symfony 2.3, вы уже не можете просто установить токен в контексте безопасности. Вам также нужно сохранить токен в сеансе.
Предположим, файл безопасности с брандмауэром:
// app/config/security.yml
security:
firewalls:
main:
//firewall settings here
И подобное действие контроллера тоже:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;
class LoginController extends Controller{
public function registerAction()
{
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.context')->setToken($token);
$this->get('session')->set('_security_main',serialize($token));
//Now you can redirect where ever you need and the user will be logged in
}
}
Для создания маркера вы захотите создать UsernamePasswordToken
, который принимает 4 параметра: Пользовательский объект, Пользовательские учетные данные, Имя брандмауэра, Роли пользователей. Вам не нужно предоставлять учетные данные пользователя, чтобы токен был действительным.
Im не на 100% уверен, что установка маркера на security.context
необходима, если вы просто собираетесь перенаправить сразу. Но, похоже, он не пострадал, поэтому я оставил его.
Затем важная часть, задающая переменную сеанса. Соглашение об именах переменных указано _security_
, за которым следует ваше имя брандмауэра, в этом случае main
делает _security_main
Наконец-то выложите это.
После регистрации пользователя вы должны иметь доступ к объекту instanceof того, что вы установили в качестве своего пользовательского объекта в настройках вашего провайдера. Решение состоит в том, чтобы создать новый токен с этим пользовательским объектом и передать его в контекст безопасности. Вот пример, основанный на моей настройке:
RegistrationController.php:
$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);
Где main
- имя брандмауэра для вашего приложения (спасибо @Joe). Это действительно все для этого; система теперь считает, что ваш пользователь полностью зарегистрирован как пользователь, которого только что создал.
EDIT: за комментарий @Miquel, я обновил образец кода контроллера, чтобы включить разумную роль по умолчанию для нового пользователя (хотя, очевидно, это можно настроить в соответствии с вашими конкретными потребностями).
Я использую Symfony 2.2, и мой опыт немного отличался от Проблемных, так что это комбинированная версия всей информации из этого вопроса плюс некоторые из моих собственных,
Я думаю, что Joe неверен относительно значения $providerKey
, третьего параметра для конструктора UsernamePasswordToken
. Он должен быть ключом провайдера аутентификации (не пользователя). Он используется системой аутентификации для различения токенов, созданных для разных поставщиков. Любой поставщик, который спускается из UserAuthenticationProvider
, будет только проверять токены, чей ключ поставщика соответствует его собственному. Например, UsernamePasswordFormAuthenticationListener
устанавливает ключ маркера, который он создает, чтобы соответствовать его соответствующему DaoAuthenticationProvider
. Это позволяет одному брандмауэру иметь несколько провайдеров имени пользователя и пароля без их перехода друг на друга. Поэтому нам нужно выбрать ключ, который не будет конфликтовать с другими поставщиками. Я использую 'new_user'
.
У меня есть несколько систем в других частях моего приложения, которые зависят от события успешной проверки подлинности, и это не срабатывает просто установка токена в контексте. Я должен был получить EventDispatcher
из контейнера и запустить событие вручную. Я также решил не запускать интерактивное событие входа, потому что мы аутентифицируем пользователя неявно, а не в ответ на явный запрос на вход.
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
$user = // get a Symfony user instance somehow
$token = new UsernamePasswordToken(
$user, null, 'new_user', $user->getRoles() );
$this->get( 'security.context' )->setToken( $token );
$this->get( 'event_dispatcher' )->dispatch(
AuthenticationEvents::AUTHENTICATION_SUCCESS,
new AuthenticationEvent( $token ) );
Обратите внимание, что использование $this->get( .. )
предполагает, что фрагмент находится в методе контроллера. Если вы используете код где-то еще, вам придется изменить их, чтобы вызвать ContainerInterface::get( ... )
таким образом, который подходит для среды. Так как это происходит, мои пользовательские объекты реализуют UserInterface
, поэтому я могу использовать их непосредственно с токеном. Если вы этого не сделаете, вам придется найти способ конвертировать их в экземпляры UserInterface
.
Этот код работает, но я чувствую, что он взламывает архитектуру аутентификации Symfony, а не работает с ней. Вероятно, было бы правильнее внедрить новый поставщик аутентификации со своим собственным классом токенов, а не захватить UsernamePasswordToken
. Кроме того, использование надлежащего провайдера означает, что события были обработаны для вас.
Если у вас есть объект UserInterface (и это должно быть в большинстве случаев), вы можете использовать функцию getRoles, которую она реализует для последнего аргумента. Поэтому, если вы создаете функцию logUser, она должна выглядеть так:
public function logUser(UserInterface $user) {
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->container->get('security.context')->setToken($token);
}
В случае, если у кого-то есть один и тот же следующий вопрос, который заставлял меня возвращаться сюда:
Вызов
$this->container->get('security.context')->setToken($token);
влияет только на текущий security.context
для используемого маршрута.
т.е. вы можете только войти в систему пользователя с URL-адреса в элементе управления брандмауэром.
(добавьте исключение для маршрута, если необходимо - IS_AUTHENTICATED_ANONYMOUSLY
)
Я попробовал все ответы здесь, и никто не работал. Единственный способ, которым я мог аутентифицировать своих пользователей на контроллере, - это сделать подзапрос, а затем перенаправить. Вот мой код, я использую 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'));
Как уже упоминалось, этот неуловимый параметр $providerKey на самом деле является не чем иным, как именем вашего правила брандмауэра "foobar" в примере ниже.
firewalls:
foobar:
pattern: /foo/
blablabla
качестве третьего параметра UsernamePasswordToken, это тоже будет работать? что означает этот параметр?
В Symfony версии 2.8.11 (возможно, для старых и новых версий) , если вы используете FOSUserBundle, просто выполните следующее:
try {
$this->container->get('fos_user.security.login_manager')->loginUser(
$this->container->getParameter('fos_user.firewall_name'), $user, null);
} catch (AccountStatusException $ex) {
// We simply do not authenticate users which do not pass the user
// checker (not enabled, expired, etc.).
}
Не нужно отправлять события, как я видел в других решениях.
вызванный из FOS\UserBundle\Controller\RegistrationController:: authenticateUser
(from composer.json FOSUserBundle версия: "friendsofsymfony/user-bundle": "~ 1.3" )
$this->get('session')->set('_security_main', serialize($token));
, Спасибо, @Chausser!