Doctrine mécanisme d'authentification basé dans le projet Symfony2
-
11-10-2019 - |
Question
Je travaille sur un petit projet soutenu par Doctrine2 en utilisant Symfony2 pour la première fois. À l'heure actuelle, je suis aux prises avec la composante de sécurité de Symfony2, pour être exact avec le mécanisme d'authentification décrit dans la documentation .
Je veux utiliser une authentification basée sur des formulaires et a tout indiqué dans la documentation:
J'ai un fichier de configuration security.yml qui ressemble à ceci:
security.config:
firewalls:
admin:
pattern: /admin/.*
form-login: true
logout: true
login_path: /login
check_path: /validateLogin
always_use_default_target_path: false
target_path_parameter: target
check_page:
pattern: /validateLogin
form-login: true
login_path: /login
check_path: /validateLogin
always_use_default_target_path: false
target_path_parameter: target
public:
pattern: /.*
security: false
providers:
admin:
password_encoder: md5
entity:
class: AdminBundle:User
property: username
access_control:
- { path: /admin/.*, role: ROLE_ADMIN }
- { path: /validateLogin, role: IS_AUTHENTICATED_ANONYMOUSLY }
role_hierarchy:
ROLE_ADMIN: ROLE_USER
Le check_page est exclu de la zone « secureless » après avoir lu un fil similaire sur devcomments .
Dans ma configuration de routage I inclut deux règles pour l'authentification:
_security_login:
pattern: /login
defaults:
_controller: PublicBundle:Auth:index
_security_check:
pattern: /validateLogin
La classe d'entité j'utilise pour représenter un utilisateur est une entité Doctrine2 et met en œuvre le AccountInterface:
<?php
namespace Application\AdminBundle\Entity;
use Symfony\Component\Security\User\AccountInterface;
/**
* @orm:Entity
*/
class User implements AccountInterface
{
/**
* @orm:Id
* @orm:Column(type="integer")
* @orm:GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @orm:Column(type="string", length="255")
*/
protected $username;
/**
* @orm:Column(type="string", length="40")
*/
protected $password;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
/**
* Implementing the AccountInterface interface
*/
public function __toString()
{
return $this->getUsername();
}
public function getRoles()
{
return array('ROLE_ADMIN');
}
public function eraseCredentials()
{
}
public function getSalt()
{
return $this->getId();
}
}
Dans la classe AuthController J'utilise le code exemple des documents de Symfony2:
public function indexAction()
{
if ($this->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
}
return
$this->render(
'PublicBundle:Auth:index.twig',
array(
'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME),
'error' => $error));
}
Maintenant vient le problème: La règle de redirection de http: //symfony2.localhost/app_dev.php/admin/test http: travaux de //symfony2.localhost/app_dev.php/login mais après avoir entré le nom d'utilisateur / mot de passe et soumission le formulaire de connexion, je suis redirigé vers l'URL de connexion à nouveau sans message d'erreur.
Je sais que c'est probablement une question vraiment fondamentale, mais puisqu'il n'y a pas encore beaucoup de documentation sur Symfony2, je pense que c'est un bon endroit pour poser des questions comme celle-ci. En général, il y a quelques points à l'intérieur d'un projet de Symfony2 qui semblent fonctionner comme par magie (bien sûr soutenu par DI-) qui rendent le processus d'apprentissage un peu difficile. Mes réflexions sur la façon dont l'authentification fonctionne est qu'il existe un contrôleur magique qui attire l'action validateLogin, cherche un référentiel d'entité pour mon entité utilisateur, appelle findOneBy ( « username » => $ username) et compare les mots de passe ... est-ce droit?
Je vous remercie à l'avance pour tout soupçon, je suis googler cette question pour plus de quelques heures maintenant ... :)
Paul
La solution
Mes pensées sur la façon dont l'authentification fonctionne est qu'il existe un contrôleur magique qui attire l'action validateLogin, cherche un référentiel d'entité pour mon entité utilisateur, appelle findOneBy ( « username » => $ username) et compare les mots de passe .. . est ce droit?
Vous avez tort. L'authentification ne comporte aucun contrôleur, qui est la raison pour laquelle vous ne spécifiez pas dans l'itinéraire de _security_check
. Auth est basé sur EventDispatcher
. Chaque fois que vous spécifiez un certain auditeur dans votre pare-feu (par exemple. form_login
, anonymous
, logout
etc.) vous vous inscrivez en fait un nouvel écouteur pour l'événement core.security
. Symfony\Component\HttpKernel\Security\Firewall::handle()
est un endroit où ces auditeurs sont effectivement enregistrés.
Le général, le débit simplifié:
- L'utilisateur remplit formulaire de connexion (
_username
et champs de_password
). - demande est traitée par Symfony2.
- événement
core.security
est tiré. - EventDispatcher informe tous les auditeurs.
-
UsernamePasswordFormAuthenticationListener
est tiré (méthodehandle()
) et vérifie si:- URL correspond à l'option
check_path
. - Demande a des paramètres de
_username
et_password
.
- URL correspond à l'option
- Listener tente d'authentifier l'utilisateur (méthode de
attemptAuthentication()
). - incendies de gestionnaire d'authentification tous les fournisseurs enregistrés.
- Enfin,
DaoAuthenticationProvider
est tiré et il tente de récupérer utilisateur en utilisant la classe de référentiel utilisateur de Doctrine. - Si tout va bien
UsernamePasswordToken
(qui contient l'objet$user
retourné par la méthodeloadUserByUsername()
) est retourné et l'utilisateur est redirigé.
En effet le mécanisme de sécurité est assez complexe et difficile à comprendre (la documentation ne sont pas encore terminé). Mais quand vous comprenez enfin comment cela fonctionne, alors vous verrez à quel point il est puissant mécanisme.
J'ai écrit mon propre mécanisme d'authentification et il fonctionne très bien.
-
Configuration:
J'utilise fournisseur personnalisé et encodeur.
security.config: providers: main: id: project.user_repository # DI id. Doctrine's UserRepositry check_path: /login-check encoders: main: class: Project\SiteBundle\Entity\User id: security.encoder.sha512 # DI id. Service %security.encoder.digest.class% (with "sha512" as first parameter) firewalls: restricted: pattern: /panel/.* form_login: check_path: /login-check public: pattern: /.* anonymous: true form_login: check_path: /login-check logout: true access_control: - { path: /panel/.*, role: ROLE_USER } - { path: /.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
Comme vous pouvez le voir
/panel/*
est limité, alors que/*
est public. -
Service
security.encoder.sha512
est un encodeur intégré:<service id="security.encoder.sha512" class="%security.encoder.digest.class%"> <argument>sha512</argument> </service>
-
Project\SiteBundle\Entity\User
:/** * @orm:Entity(repositoryClass="Project\SiteBundle\Repository\UserRepository") */ class User implements AdvancedAccountInterface { /** * @orm:Id @orm:Column(type="integer") * @orm:GeneratedValue(strategy="AUTO") */ protected $id; /** * @orm:Column(unique=true, nullable=true) */ protected $email; /** * @orm:Column(unique=true, nullable=true) */ protected $xmpp; /** * @orm:Column(length=128) */ protected $password; /** * @orm:Column(length=16) */ protected $salt; // User can be logged in using email address or xmpp adress. // Dozens of getters/setters here. }
-
Project\SiteBundle\Repository\UserRepository
class UserRepository extends EntityRepository implements UserProviderInterface { public function loadUserByUsername($username) { $dql = sprintf(' SELECT u FROM %s u WHERE u.email = :id OR u.xmpp = :id ', $this->_entityName); $user = null; try { $user = $this->_em->createQuery($dql)->setParameter('id', $username)->getSingleResult(); } catch (ORMException $e) { throw new UsernameNotFoundException("User $username not found.", $e->getCode(), $e); } return $user; } public function loadUserByAccount(AccountInterface $user) { return $this->loadUserByUsername($user->getUsername()); } }
-
Les itinéraires de sécurité et le contrôleur est la même que la vôtre.
Autres conseils
Vous devez utiliser les https://github.com/FriendsOfSymfony/FOSUserBundle FOS UserBundle, il met en œuvre tout cela avec Doctrine 2 et a des tonnes de fonctionnalités.
La raison, essentiellement, pourquoi les charges de page de connexion sans message d'erreur est parce que, ironiquement, vos paramètres de sécurité ne sont pas mis en place pour permettre l'accès anonyme à la page de connexion.