Domanda

Sto lavorando su un piccolo progetto Doctrine2-backed usando Symfony2 per la prima volta. Attualmente sto lottando con la componente di sicurezza di Symfony2, per l'esattezza con il meccanismo di autenticazione descritto nella documentazione .

Vorrei utilizzare l'autenticazione basata su form e ha fatto tutto indicato nei documenti:

Ho un file di configurazione security.yml che assomiglia a questo:

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

Il check_page è esclusa dalla zona "secureless" dopo aver letto un thread simile devcomments .

Nella mia configurazione di routing ho includono due regole per l'autenticazione:

_security_login:
    pattern:                      /login
    defaults:    
        _controller:              PublicBundle:Auth:index

_security_check:
    pattern:                      /validateLogin

La classe di entità che sto usando per rappresentare un utente è un'entità Doctrine2 e implementa 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();
}
}

Nella classe AuthController Sto usando il codice di esempio dai documenti di 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));
}

Ora arriva il problema: La regola di reindirizzamento da http: //symfony2.localhost/app_dev.php/admin/test di http: opere //symfony2.localhost/app_dev.php/login ma dopo aver inserito nome utente / password e compila il form di login, mi viene reindirizzato all'URL di login di nuovo senza un messaggio di errore.

So che questo è probabilmente una questione veramente di base, ma dal momento che non v'è ancora molta documentazione su Symfony2, credo che questo sia un buon posto per fare domande come questa. In generale, ci sono alcuni punti all'interno di un progetto Symfony2 che sembrano funzionare magicamente (ovviamente DI-backed) che rendono il processo di apprendimento un po 'difficile. I miei pensieri su come funziona l'autenticazione è che v'è una certa magica controller che cattura l'azione validateLogin, cerca un repository di entità per la mia entità User, chiama findOneBy ( 'username' => $ username) e mette a confronto le password ... è questo giusto?

Grazie in anticipo per qualsiasi suggerimento, ho googling questo problema per più alcune ore ormai ... :)

Paul

È stato utile?

Soluzione

I miei pensieri su come funziona l'autenticazione è che v'è una certa magica controller che cattura l'azione validateLogin, cerca un repository di entità per la mia entità User, chiama findOneBy ( 'username' => $ username) e mette a confronto le password .. . è questo diritto?

Ti sbagli. L'autenticazione non comporta alcun controllo, che è il motivo per cui non si specifica alcun percorso in _security_check. Auth si basa su EventDispatcher . Ogni volta che si specifica una certa ascoltatore nel firewall (ad es. form_login, anonymous, logout ecc) è in realtà registra un nuovo listener per l'evento core.security. Symfony\Component\HttpKernel\Security\Firewall::handle() è un luogo dove questi ascoltatori sono in realtà registrati.

Il generale, di flusso semplificato:

  1. utente riempie form di login (_username e campi _password).
  2. richiesta viene gestita da Symfony2.
  3. evento core.security viene licenziato.
  4. EventDispatcher notifica a tutti gli ascoltatori.
  5. UsernamePasswordFormAuthenticationListener viene licenziato (metodo handle()) e verifica se:
    1. URL corrisponde opzione check_path.
    2. Richiesta ha entrambi i parametri _username e _password.
  6. Listener cerca di utente autenticazione (metodo attemptAuthentication()).
  7. Gestore incendi di autenticazione tutti i fornitori registrati.
  8. Infine, DaoAuthenticationProvider viene licenziato e cerca di recuperare utente utilizzando classe repository utente di Doctrine.
  9. Se tutto va bene UsernamePasswordToken (che contengono oggetto $user restituito dal metodo loadUserByUsername()) viene restituito e l'utente viene reindirizzato.

In effetti meccanismo di sicurezza è piuttosto complessa e difficile da comprendere (documentazione non è ancora finito). Ma quando finalmente capito come funziona, allora vedrete come potente meccanismo sia.


Ho scritto il mio meccanismo di autenticazione e funziona benissimo.

  1. Configurazione:

    sto usando provider personalizzato ed encoder.

    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 }
    

    Come si può vedere /panel/* è limitato, mentre /* è pubblico.

  2. Servizio security.encoder.sha512 è un built-in encoder:

    <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. percorsi di sicurezza e di controllo sono uguali ai suoi.

Altri suggerimenti

Si dovrebbe utilizzare le https://github.com/FriendsOfSymfony/FOSUserBundle FOS UserBundle, implementa tutto questo con Doctrine 2 e ha tonnellate di caratteristiche.

La ragione, in sostanza, il motivo per cui la pagina di accesso carichi di nuovo con alcun messaggio di errore è perché, ironia della sorte, le impostazioni di protezione non sono impostati per consentire l'accesso anonimo alla pagina di login.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top