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

Était-ce utile?

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é:

  1. L'utilisateur remplit formulaire de connexion (_username et champs de _password).
  2. demande est traitée par Symfony2.
  3. événement core.security est tiré.
  4. EventDispatcher informe tous les auditeurs.
  5. UsernamePasswordFormAuthenticationListener est tiré (méthode handle()) et vérifie si:
    1. URL correspond à l'option check_path.
    2. Demande a des paramètres de _username et _password.
  6. Listener tente d'authentifier l'utilisateur (méthode de attemptAuthentication()).
  7. incendies de gestionnaire d'authentification tous les fournisseurs enregistrés.
  8. Enfin, DaoAuthenticationProvider est tiré et il tente de récupérer utilisateur en utilisant la classe de référentiel utilisateur de Doctrine.
  9. Si tout va bien UsernamePasswordToken (qui contient l'objet $user retourné par la méthode loadUserByUsername()) 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.

  1. 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.

  2. Service security.encoder.sha512 est un encodeur intégré:

    <service id="security.encoder.sha512" class="%security.encoder.digest.class%">
        <argument>sha512</argument>
    </service>
    
  3. 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.
    }
    
  4. 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());
        }
    }
    
  5. 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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top