자동 등록 후 사용자 인증


114

우리는 Symfony 2에서 처음부터 비즈니스 앱을 구축하고 있으며 사용자 등록 흐름에 약간의 문제가 있습니다. 사용자가 계정을 만든 후에는 대신 해당 자격 증명으로 자동 로그인되어야합니다. 즉시 자격 증명을 다시 제공해야합니다.

누구든지 이것에 대한 경험이 있거나 올바른 방향으로 나를 가리킬 수 있습니까?

답변:


146

심포니 4.0

이 프로세스는 심포니 3에서 4로 변경되지 않았지만 여기에 새로 권장되는 AbstractController를 사용한 예제가 있습니다. security.token_storagesession서비스는 모두 부모 getSubscribedServices메서드에 등록되어 있으므로 컨트롤러에 추가 할 필요가 없습니다.

use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use YourNameSpace\UserBundle\Entity\User;

class LoginController extends AbstractController{

    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->container->get('security.token_storage')->setToken($token);
        $this->container->get('session')->set('_security_main', serialize($token));
        // The user is now logged in, you can redirect or do whatever.
    }

}

Symfony 2.6.x-Symfony 3.0.x

심포니 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에서 확인할 수 있습니다.

심포니 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 가지 매개 변수를 허용합니다. 토큰이 유효하기 위해 사용자 자격 증명을 제공 할 필요가 없습니다.

security.context즉시 리디렉션하려는 경우 토큰 설정 이 필요하다고 100 % 확신하지 않습니다 . 하지만 아프지 않은 것 같아서 떠났습니다.

그런 다음 중요한 부분은 세션 변수를 설정하는 것입니다. 변수 이름 지정 규칙 _security_뒤에 방화벽 이름 main이옵니다._security_main


1
User가 성공적으로 로그 한 코드를 구현했지만 $ this-> getUser () 객체가 NULL을 반환합니다. 어떤 아이디어?
sathish

2
미친 일이없이 일어나고 있었다 $this->get('session')->set('_security_main', serialize($token));. 감사합니다, @Chausser!
Dmitriy

1
Symfony 2.6에서 이름이 지정된 방화벽에 대한 토큰을 설정하고 이름이 지정된 main다른 방화벽 admin(사용자를 가장하는 것처럼)으로 인증을 _security_admin받으면 이상한 일이 발생합니다. UsernamePasswordToken즉, 제공 한 사용자와 함께 가져옵니다 . 즉, "연결 해제"됩니다. 당신의 admin방화벽. "관리자"방화벽에 대한 토큰을 유지하는 방법을 아십니까?
gremo

1
솔직히 말해서 당신이 동시에 2 개의 방화벽에 대해 인증을받을 수 있을지 확신이 서지 않습니다.하지만
Chase

3
@Chausser가 작동하도록 관리했습니다. 당신의 대답은 완벽하게 옳습니다 (그리고 업데이트되었습니다), 동일한 대상 방화벽setToken(..) 에서 호출 하거나 아직 인증 되지 않은 경우에만 작동합니다 .
gremo

65

마침내 이것을 알아 냈습니다.

사용자 등록 후 공급자 구성에서 사용자 엔터티로 설정 한 개체 인스턴스에 액세스 할 수 있어야합니다. 해결책은 해당 사용자 엔터티로 새 토큰을 만들고이를 보안 컨텍스트에 전달하는 것입니다. 내 설정을 기반으로 한 예는 다음과 같습니다.

RegistrationController.php:

$token = new UsernamePasswordToken($userEntity, null, 'main', array('ROLE_USER'));
$this->get('security.context')->setToken($token);

main응용 프로그램의 방화벽 이름은 어디에 있습니까 (@Joe에게 감사드립니다). 그게 전부입니다. 이제 시스템은 사용자가 방금 생성 한 사용자로 완전히 로그인 한 것으로 간주합니다.

편집 : @Miquel의 의견에 따라 새 사용자를위한 합리적인 기본 역할을 포함하도록 컨트롤러 코드 샘플을 업데이트했습니다 (당연히 애플리케이션의 특정 요구에 따라 조정할 수 있음).


2
이것은 Symfony 2의 릴리스 버전에서는 옳지 않습니다. 사용자의 역할을 UsernamePasswordToken 생성자에 대한 네 번째 인수로 전달해야합니다. 그렇지 않으면 인증되지 않은 것으로 표시되고 사용자에게는 역할이 없습니다.
Michael

"기억하기"플래그는 어떻습니까? 사용자를 직접 로그인하는 방법은 물론 영원히 로그인해야합니다. 이 코드는 그 문제를 해결하지 못합니다.
멕포 2012 년

@maectpo는 내 원래 요구 사항의 범위에 포함되지 않았지만 훌륭한 후속 답변처럼 들립니다. 당신이 무엇을 생각하는지 알려주십시오.
문제가있는

문제가 있습니다. 이 방법으로 로그인 할 수 있지만 app.user 변수가 비어 있습니다. 이 로그인 프로세스에서이 변수를 채우는 방법을 알고 있습니까? -참조 : api.symfony.com/2.0/Symfony/Component/Security/Core/…
unairoldan

1
마크 아래 말했듯이, 당신은 UsernamePasswordToken 네임 스페이스를 등록해야합니다 :use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
MrGlass

6

UserInterface 객체가있는 경우 (대부분의 경우) 마지막 인수에 대해 구현하는 getRoles 함수를 사용할 수 있습니다. 따라서 logUser 함수를 생성하면 다음과 같아야합니다.

public function logUser(UserInterface $user) {
    $token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
    $this->container->get('security.context')->setToken($token);
}

6

저는 Symfony 2.2를 사용하고 있으며 내 경험은 Problematic의 경험과 약간 달랐습니다. 때문에이 질문의 모든 정보와 내 자신의 정보를 결합한 버전입니다.

생성자 의 세 번째 매개 변수 인의 값에 대해 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의 인증 아키텍처를 사용하는 것보다 해킹하는 것 같습니다. .NET Framework를 하이재킹하는 것보다 자체 토큰 클래스 로 새 인증 공급자구현하는 것이 더 정확할 것입니다 UsernamePasswordToken. 또한 적절한 공급자를 사용하면 이벤트가 처리되었음을 의미합니다.


4

누군가 나를 여기로 다시 돌아 오게 한 동일한 후속 질문이있는 경우 :

부름

$this->container->get('security.context')->setToken($token); 

security.context사용 된 경로 의 전류에만 영향을 미칩니다 .

즉, 방화벽이 제어하는 ​​URL에서만 사용자로 로그인 할 수 있습니다.

(필요한 경우 경로에 대한 예외 추가- IS_AUTHENTICATED_ANONYMOUSLY)


1
세션에서 이것을 어떻게하는지 아십니까? 현재 요청이 아니라?
Jake N

3

Symfony 4.4를 사용하면 컨트롤러 메소드에서 다음을 수행 할 수 있습니다 (Symfony 문서에서 참조 : https://symfony.com/doc/current/security/guard_authentication.html#manually-authenticating-a-user ).

// src/Controller/RegistrationController.php
// ...

use App\Security\LoginFormAuthenticator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;

class RegistrationController extends AbstractController
{
    public function register(LoginFormAuthenticator $authenticator, GuardAuthenticatorHandler $guardHandler, Request $request)
    {
        // ...

        // after validating the user and saving them to the database
        // authenticate the user and use onAuthenticationSuccess on the authenticator
        return $guardHandler->authenticateUserAndHandleSuccess(
            $user,          // the User object you just created
            $request,
            $authenticator, // authenticator whose onAuthenticationSuccess you want to use
            'main'          // the name of your firewall in security.yaml
        );
    }
}

한 가지 중요한 것은 방화벽이로 설정되어 있지 않은지 확인하는 것 lazy입니다. 그렇다면 토큰은 세션에 저장되지 않으며 로그인되지도 않습니다.

firewalls:
    main:
        anonymous: ~ # this and not 'lazy'

2

여기에서 이미 언급했듯이이 애매한 $ providerKey 매개 변수는 실제로 방화벽 규칙의 이름 인 'foobar'(아래 예의 경우)에 지나지 않습니다.

firewalls:
    foobar:
        pattern:    /foo/

예를 들어 blablablaUsernamePasswordToken에 세 번째 매개 변수로 문자열을 전달하면 왜 작동하는지 설명해 주 시겠습니까? 이 매개 변수는 무엇을 의미합니까?
Mikhail

1
이 매개 변수는 토큰을 특정 방화벽 공급자에 바인딩합니다. 대부분의 경우 공급자는 한 명뿐이므로 신경 쓰지 마십시오.
Gottlieb Notschnabel

2

나는 여기에서 모든 대답을 시도했지만 아무것도 작동하지 않았습니다. 컨트롤러에서 사용자를 인증 할 수있는 유일한 방법은 하위 요청을 만든 다음 리디렉션하는 것입니다. 다음은 내 코드입니다. 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'));

1

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에서 영감을 얻었습니다.

(composer.json FOSUserBundle 버전에서 : "friendsofsymfony / user-bundle": "~ 1.3")

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.