2012-03-03 2 views
46

Я хотел бы зарегистрировать пользователя сразу после процесса регистрации, не пропуская регистрационную форму.Как программировать логин/аутентификацию пользователя?

Возможно ли это? Я нашел решение с FOSUserBundle, но я не использую его в проекте, над которым я фактически работаю.

Вот мой security.yml, я работаю с двумя брандмауэрами. Простой текстовый кодировщик предназначен только для тестирования.

security: 
    encoders: 
     Symfony\Component\Security\Core\User\User: plaintext 
     Ray\CentralBundle\Entity\Client: md5 

    role_hierarchy: 
     ROLE_ADMIN:  ROLE_USER 
     ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH] 

    providers: 
     in_memory: 
      users: 
       admin: { password: admin, roles: [ 'ROLE_ADMIN' ] } 
     entity: 
      entity: { class: Ray\CentralBundle\Entity\Client, property: email } 

    firewalls: 
     dev: 
      pattern: ^/(_(profiler|wdt)|css|images|js)/ 
      security: false 

     user_login: 
      pattern: ^/user/login$ 
      anonymous: ~ 

     admin_login: 
      pattern: ^/admin/login$ 
      anonymous: ~ 

     admin: 
      pattern: ^/admin 
      provider: in_memory 
      form_login: 
       check_path: /admin/login/process 
       login_path: /admin/login 
       default_target_path: /admin/dashboard 
      logout: 
       path: /admin/logout 
       target:/

     site: 
      pattern: ^/ 
      provider: entity 
      anonymous: ~ 
      form_login: 
       check_path: /user/login/process 
       login_path: /user/login 
       default_target_path: /user 
      logout: 
       path: /user/logout 
       target:/

    access_control: 
     - { path: ^/user/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     - { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
     - { path: ^/user, roles: ROLE_USER } 
     - { path: ^/admin, roles: ROLE_ADMIN } 
     - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
+0

Если вы не используете FOSUserBundle, какой пакет вы используете? – hakre

+0

@hakre Я не использую ни одного пакета, просто пользовательский объект, который реализует UserInterface. – rayfranco

+0

Пожалуйста, добавьте вашу конфигурацию 'security:' в свой вопрос. Маска конфиденциальных значений. – hakre

ответ

85

Да, вы можете сделать это через что-то подобное следующему:

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

public function registerAction() 
{ 
    // ... 
    if ($this->get("request")->getMethod() == "POST") 
    { 
     // ... Do any password setting here etc 

     $em->persist($user); 
     $em->flush(); 

     // Here, "public" is the name of the firewall in your security.yml 
     $token = new UsernamePasswordToken($user, $user->getPassword(), "public", $user->getRoles()); 

     // For older versions of Symfony, use security.context here 
     $this->get("security.token_storage")->setToken($token); 

     // Fire the login event 
     // Logging the user in above the way we do it doesn't do this automatically 
     $event = new InteractiveLoginEvent($request, $token); 
     $this->get("event_dispatcher")->dispatch("security.interactive_login", $event); 

     // maybe redirect out here 
    } 
} 

Событие стрельбы в конце не выполняется автоматически при установке маркера в контексте, в то время как это было бы обычно при использовании, например, формы входа или подобного. Отсюда и причина включения его здесь. Вам может потребоваться настроить тип используемого токена, в зависимости от вашего варианта использования - приведенный выше UsernamePasswordToken является основным токеном, но при необходимости вы можете использовать другие.

Редактировать: Скорректированный приведенный выше код, чтобы объяснить параметр «public», а также добавить роли пользователя в создание маркера на основе комментария Франко ниже.

+0

Спасибо за этот ответ. Кажется, это правильный путь, но на самом деле это не работает. Ссылаясь на мое последнее изменение (security.yml), я изменил operatorKey (где ваш «общедоступный» для «сущности»), но я не уверен, что сделал все правильно. Когда вы говорите «вам может понадобиться настроить тип токена», я не уверен, что понимаю. Я искал [здесь] (http://api.symfony.com/2.0/Symfony/Component/Security/Core/Authentication/Token/UsernamePasswordToken.html) Спасибо за помощь. – rayfranco

+2

Я нашел помощь по [этой теме] (http://stackoverflow.com/questions/5886713/automatic-post-registration-user-authentication) и, наконец, нашел, что было не так. ** Третий параметр - это имя брандмауэра **, и необходим четвертый параметр, представляющий собой массив ролей для токена. [Это сработало для меня] (https://gist.github.com/1974236) – rayfranco

+0

@FrancoBouly ah отличный материал, рад, что у вас это работает! Я думаю, что параметр брандмауэра может потребоваться отправить обратно в документацию, поскольку я не мог вспомнить, почему я сделал это изначально :-) Если я не пропустил полностью прочитать его ;-) – richsage

2

Принятая версия не будет работать с symfony 3.3. Пользователь будет аутентифицирован в следующем запросе вместо текущего. Причина в том, что ContextListener проверяет существование предыдущего сеанса, и если он не существует, он очистит защиту TokenStorage. Единственный путь вокруг этого (хакерский, как ад) - подделка существования предыдущего сеанса, вручную инициализируя сеанс (и cookie) в текущем запросе.

Сообщите мне, если вы найдете лучшее решение.

BTW Я не уверен, что это должно быть объединено с принятым решением.

private function logUserIn(User $user) 
{ 
    $token = new UsernamePasswordToken($user, null, "common", $user->getRoles()); 
    $request = $this->requestStack->getMasterRequest(); 

    if (!$request->hasPreviousSession()) { 
     $request->setSession($this->session); 
     $request->getSession()->start(); 
     $request->cookies->set($request->getSession()->getName(), $request->getSession()->getId()); 
    } 

    $this->tokenStorage->setToken($token); 
    $this->session->set('_security_common', serialize($token)); 

    $event = new InteractiveLoginEvent($this->requestStack->getMasterRequest(), $token); 
    $this->eventDispatcher->dispatch("security.interactive_login", $event); 
} 

Приведенный выше код предполагает, что ваше имя брандмауэра (или общее имя контекста) является common.

+0

Правильный способ - установить require_previous_session в false в брандмауэре 'form_login: require_previous_session: false' –

+0

Я бы хотел проверить. Но у меня смутная память, что я пробовал это, и это не помогло. – pinkeen

0

Попробуйте это: For Symfony 3 users, не забудьте сделать эту поправку, чтобы проверить равенство паролей (как метод, показанный для проверки пароля на этой ссылке не работает):

$current_password = $user->getPassword(); 
$user_entry_password = '_got_from_a_form'; 

$factory = $this->get('security.encoder_factory'); 
$encoder = $factory->getEncoder($user); 
$password = $encoder->encodePassword($user_entry_password, $user->getSalt()); 

if(hash_equals($current_password, $password)){ 
//Continue there 
} 

// I hash the equality process for more security 

+ информация: hash_equals_function

 Смежные вопросы

  • Нет связанных вопросов^_^