it-swarm.com.de

Automatische Benutzerauthentifizierung nach der Registrierung

Wir bauen eine Business-App in Symfony 2 von Grund auf auf, und ich bin mit dem Ablauf der Benutzerregistrierung in Schwierigkeiten geraten: Nachdem der Benutzer ein Konto erstellt hat, sollte er stattdessen automatisch mit diesen Anmeldeinformationen angemeldet werden sofort gezwungen werden, ihre Nachweise erneut bereitzustellen.

Hat jemand Erfahrung damit gemacht oder mich in die richtige Richtung weisen können?

106
Problematic

Symfony 4.0

Dieser Prozess hat sich nicht von Symfony 3 auf 4 geändert, aber hier ist ein Beispiel mit dem neu empfohlenen AbstractController. Sowohl der security.token_storage- als auch der session-Service sind in der übergeordneten getSubscribedServices-Methode registriert, sodass Sie diese nicht in Ihrem Controller hinzufügen müssen.

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

Ab Symfony 2.6 ist security.context zugunsten von security.token_storage veraltet. Der Controller kann jetzt einfach sein:

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));
    }

}

Während dies veraltet ist, können Sie security.context weiterhin verwenden, da es abwärtskompatibel gemacht wurde. Seien Sie einfach bereit, es für Symfony 3 zu aktualisieren

Weitere Informationen zu den 2.6 Sicherheitsänderungen finden Sie hier: https://github.com/symfony/symfony/blob/2.6/UPGRADE-2.6.md

Symfony 2.3.x

Um dies in Symfony 2.3 zu erreichen, können Sie das Token nicht mehr nur im Sicherheitskontext festlegen. Sie müssen das Token auch in der Sitzung speichern.

Angenommen, eine Sicherheitsdatei mit einer Firewall wie:

// app/config/security.yml
security:
    firewalls:
        main:
            //firewall settings here

Und eine Controller-Aktion ähnlich auch:

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
    }

}

Für die Tokenerstellung sollten Sie eine UsernamePasswordToken erstellen. Dies akzeptiert 4 Parameter: Benutzerentität, Benutzeranmeldeinformationen, Firewall-Name und Benutzerrollen. Sie müssen keine Benutzeranmeldeinformationen angeben, damit das Token gültig ist.

Ich bin nicht 100% sicher, dass das Setzen des Tokens für den security.context erforderlich ist, wenn Sie sofort umleiten möchten. Aber es scheint nicht weh zu tun, also habe ich es gelassen.

Dann der wichtige Teil, die Sitzungsvariable einzustellen. Die Namenskonvention für Variablen lautet _security_, gefolgt von Ihrem Firewall-Namen. In diesem Fall main, um _security_main zu erstellen.

138
Chase

Endlich herausgefunden.

Nach der Benutzerregistrierung sollten Sie Zugriff auf eine Objektinstanz haben, die Sie in Ihrer Providerkonfiguration als Benutzerentität festgelegt haben. Die Lösung besteht darin, mit dieser Benutzerentität ein neues Token zu erstellen und dieses in den Sicherheitskontext zu übergeben. Hier ist ein Beispiel, das auf meinem Setup basiert:

RegistrationController.php:

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

Dabei ist main der Name der Firewall für Ihre Anwendung (Danke, @Joe). Das ist wirklich alles, was dazu gehört. Das System betrachtet Ihren Benutzer nun als vollständig angemeldet als den Benutzer, den er gerade erstellt hat.

BEARBEITEN: Per @ Miquels Kommentar: Ich habe das Controller-Codebeispiel so aktualisiert, dass es eine sinnvolle Standardrolle für einen neuen Benutzer enthält (obwohl dies natürlich an die spezifischen Anforderungen Ihrer Anwendung angepasst werden kann).

65
Problematic

Ich verwende Symfony 2.2 und meine Erfahrung war etwas anders als bei Problematic . Dies ist eine kombinierte Version aller Informationen dieser Frage und einiger meiner eigenen. 

Ich denke, Joe ist falsch in Bezug auf den Wert von $providerKey, dem dritten Parameter des Konstruktors UsernamePasswordToken. Es soll der Schlüssel eines Authentifizierungsanbieters (nicht eines Benutzers) sein. Sie wird vom Authentifizierungssystem verwendet, um zwischen Token zu unterscheiden, die für verschiedene Anbieter erstellt wurden. Jeder Provider, der von UserAuthenticationProvider abstammt, authentifiziert nur Token, deren Providerschlüssel mit seinem eigenen übereinstimmt. Beispielsweise setzt UsernamePasswordFormAuthenticationListener den Schlüssel des von ihm erstellten Tokens so, dass er dem Schlüssel des entsprechenden DaoAuthenticationProvider entspricht. Auf diese Weise kann eine Firewall über mehrere Benutzernamen + Kennwortanbieter verfügen, ohne dass sie aufeinander treten. Wir müssen daher einen Schlüssel wählen, der mit keinem anderen Anbieter in Konflikt steht. Ich benutze 'new_user'.

Ich habe einige Systeme in anderen Teilen meiner Anwendung, die vom authentication-Erfolgsereignis abhängen und das nicht dadurch ausgelöst wird, dass nur das Token für den Kontext festgelegt wird. Ich musste den EventDispatcher aus dem Container holen und das Ereignis manuell auslösen. Ich habe mich gegen ein interactive login -Ereignis entschieden, weil wir den Benutzer implizit authentifizieren, nicht als Antwort auf eine explizite Anmeldeanforderung.

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 ) );

Beachten Sie, dass die Verwendung von $this->get( .. ) davon ausgeht, dass das Snippet eine Controller-Methode ist. Wenn Sie den Code an anderer Stelle verwenden, müssen Sie diese ändern, um ContainerInterface::get( ... ) in einer der Umgebung angemessenen Weise aufzurufen. Zufällig implementieren meine Benutzerentitäten UserInterface, damit ich sie direkt mit dem Token verwenden kann. Wenn dies nicht der Fall ist, müssen Sie einen Weg finden, diese in UserInterface-Instanzen zu konvertieren.

Dieser Code funktioniert, aber ich denke, er hackt die Authentifizierungsarchitektur von Symfony an, anstatt mit ihr zu arbeiten. Es wäre wahrscheinlich richtiger, einen neuen Authentifizierungsanbieter mit seiner eigenen Token-Klasse zu implementieren, anstatt die Variable UsernamePasswordToken zu entführen. Die Verwendung eines geeigneten Providers bedeutet auch, dass die Ereignisse für Sie behandelt wurden.

6
Sam Hanes

Wenn Sie über ein UserInterface-Objekt verfügen (und dies sollte meistens der Fall sein), möchten Sie möglicherweise die getRoles-Funktion verwenden, die für das letzte Argument implementiert wird .. _ Wenn Sie also eine Funktion logUser erstellen, sollte dies so aussehen :

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

Falls jemand die gleiche Folgefrage hat, die mich dazu veranlasst hat, hierher zurückzukehren:

Berufung 

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

wirkt sich nur auf den aktuellen security.context für die verwendete Route aus. 

Das heißt Sie können einen Benutzer nur über eine URL innerhalb der Kontrolle der Firewall anmelden.

(Fügen Sie ggf. eine Ausnahme für die Route hinzu - IS_AUTHENTICATED_ANONYMOUSLY)

4
daemonl

Als problematisch hier bereits erwähnt, ist dieser schwer fassbare Parameter $ providerKey in Wirklichkeit nichts anderes als der Name Ihrer Firewall-Regel, im folgenden Beispiel 'foobar'.

firewalls:
    foobar:
        pattern:    /foo/
2
Nim

Ich habe alle Antworten hier ausprobiert und keine hat funktioniert. Die einzige Möglichkeit, meine Benutzer auf einem Controller zu authentifizieren, besteht darin, eine Unteranfrage zu erstellen und dann umzuleiten. Hier ist mein Code, ich verwende silex, kann aber leicht an symfony2 angepasst werden:

$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'));
2
Diego Castro

In der Symfony-Version 2.8.11 (die wahrscheinlich für ältere und neuere Versionen funktioniert), führen Sie aus, wenn Sie FOSUserBundle verwenden:

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.).
}

Ereignis muss nicht abgesendet werden, wie ich es bei anderen Lösungen gesehen habe.

eingeloggt von FOS\UserBundle\Controller\RegistrationController :: authenticateUser

(von composer.json FOSUserBundle-Version: "friendsofsymfony/user-bundle": "~ 1.3")

0
Nico