Comment se connecter/authentifier un utilisateur par programme?
Je voudrais connecter l'utilisateur juste après le processus d'inscription, sans passer par le formulaire de connexion.
Est-ce possible ? J'ai trouvé une solution avec FOSUserBundle
, mais je ne l'utilise pas sur le projet sur lequel je travaille réellement.
Voici ma sécurité.yml, je travaille avec deux pare-feu. L'encodeur de texte brut est juste pour tester.
security:
encoders:
SymfonyComponentSecurityCoreUserUser: plaintext
RayCentralBundleEntityClient: 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: RayCentralBundleEntityClient, 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 }
3 réponses
Oui, vous pouvez le faire via quelque chose de similaire à ce qui suit:
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
}
}
Le déclenchement de l'événement à la fin n'est pas automatiquement effectué lorsque vous définissez un jeton dans le contexte, alors qu'il le serait normalement lors de l'utilisation d'un formulaire de connexion ou similaire. D'où la raison de l'inclure ici. Vous devrez peut-être ajuster le type de jeton utilisé, en fonction de votre cas d'utilisation, - la UsernamePasswordToken
ci-dessus est une base de jeton, mais vous pouvez utiliser d'autres si nécessaire.
Edit: Ajusté le code ci-dessus pour expliquez le paramètre 'public' et ajoutez également les rôles de l'utilisateur dans la création du jeton, en fonction du commentaire de Franco ci-dessous.
La version acceptée ne fonctionnera pas avec symfony 3.3. L'Utilisateur sera authentifié dans la requête suivante au lieu de la requête actuelle. La raison en est que ContextListener vérifie l'existence de la session précédente et s'il n'existe pas, il effacera le tokenstorage de sécurité. La seule façon de contourner cela (hackish comme l'enfer) est de simuler l'existence de la session précédente en initialisant manuellement la session (et le cookie) sur la requête en cours.
Laissez-moi savoir si vous trouvez une meilleure solution.
BTW Je ne suis pas sûr si cela devrait être fusionné avec la solution retenue.
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);
}
Le code ci-dessus suppose que votre nom de pare-feu (ou nom de contexte partagé) est common
.
Essayez ceci: pour les utilisateurs de Symfony 3 , n'oubliez pas de faire cette correction pour tester l'égalité des mots de passe (car la méthode montrée pour tester le mot de passe sur ce lien ne fonctionne pas):
$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
+ d'infos : hash_equals_function