IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Bundle d'authentification - Solution complète


Sujet :

Symfony PHP

  1. #1
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut Bundle d'authentification - Solution complète
    Vu la taille des avirons que j'ai du sortir pour réaliser cette ébauche de module de sécurité totalement personnalisé,
    j'ai pensé qu'une publication pourrait aider d'autres galériens...

    L'objet est de faire un bundle qui présente un écran de login puis utilise un listener personnalisé,
    un authenticationProvider, un UserProvider, un User, une Factory et tout le toutim. Avec les fichiers
    de configuration qui vont bien.

    Normalement, avec une application vierge, si l'on crée un Bundle avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    php app/console generate:bundle --namespace=Acme/SecurityBundle --format=yml
    Et que l'on copie le code ci-dessous, on doit obtenir un squelette de sécurité personnalisé qui fonctionne.

    J'ai essayé autant que possible de personnaliser tout ce qui pouvait l'être.

    Toute remarque ou suggestion qui permettrait d'améliorer la clarté et/ou la qualité de ce qui est proposé ci dessous est la bienvenue.

    Tout d'abord un petit tour du circuit emprunté :

    Le premier élément qui va être créé est la Factory. C'est dans cette factory que l'on va préciser le Listener
    et le UserProvider que l'on compte utiliser. Pour que cela fonctionne, ils doivent être déclarés comme services
    dans security.yml

    Ensuite, le Listener et l'authenticationProvider seront initialisés.

    À ce moment là, tout est prêt pour lancer l'action. Le loginAction est déclenché et le formulaire affiché.

    L'action login_check lance la fonction attemptAuthentication du Listener.

    Un jeton (Token) est créé, la fonction d'authentification (authenticate) du
    UserProvider est exécutée et, en cas de succès, l'utilisateur correspondant est chargé et
    son jeton authentifié (Token) est créé.

    Et concrètement, ça donne quoi ?.

    L'arborescence du Bundle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
     
    |~src/
    | |~Acme/
    | | `~SecurityBundle/
    | |   |~Controller/
    | |   | |-DefaultController.php
    | |   | `-SecurityController.php
    | |   |~DependencyInjection/
    | |   | |~Security/
    | |   | | `~Factory/
    | |   | |   `-UserFactory.php
    | |   | |-AcmeSecurityExtension.php
    | |   | `-Configuration.php
    | |   |~Resources/
    | |   | |~config/
    | |   | | |-routing.yml
    | |   | | |-security_factories.yml
    | |   | | `-services.yml
    | |   | `~views/
    | |   |   |~Default/
    | |   |   | `-index.html.twig
    | |   |   `~Login/
    | |   |     `-login.html.twig
    | |   |~Security/
    | |   | |~Authentication/
    | |   | | |~Firewall/
    | |   | | | `-AuthListener.php
    | |   | | |~Provider/
    | |   | | | `-AuthProvider.php
    | |   | | `~Token/
    | |   | |   `-UserToken.php
    | |   | `~User/
    | |   |   |-User.php
    | |   |   `-UserProvider.php
    | |   |+Tests/
    | |   `-AcmeSecurityBundle.php
    La factory :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    <?php
    // src/Acme/SecurityBundle/DependencyInjection/Security/Factory/UserFactory.php
     
    namespace Acme\SecurityBundle\DependencyInjection\Security\Factory;
     
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Symfony\Component\DependencyInjection\Reference;
    use Symfony\Component\DependencyInjection\DefinitionDecorator;
    use Symfony\Component\Config\Definition\Builder\NodeDefinition;
    use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
     
    class UserFactory implements SecurityFactoryInterface
    {
        public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
        {
            $providerId = 'security.authentication.provider.user.'.$id;
            $container
                ->setDefinition($providerId, new DefinitionDecorator('user.security.authentication.provider'))
                ->replaceArgument(0, new Reference($userProvider))
            ;
            $listenerId = 'security.authentication.listener.user.'.$id;
            $listener = $container->setDefinition($listenerId, new DefinitionDecorator('user.security.authentication.listener'));
     
            return array($providerId, $listenerId, $defaultEntryPoint);
        }
     
        public function getPosition()
        {
            return 'pre_auth';
        }
     
        public function getKey()
        {
            return 'my_user_factory';  // Utilisée dans app/config/security.yml
        }
     
        public function addConfiguration(NodeDefinition $node)
        {}
    }
    Le Listener :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
     
    <?php
    // Acme/SecurityBundle/Security/Firewall/AuthListener.php
     
    namespace Acme\SecurityBundle\Security\Authentication\Firewall;
     
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    use Symfony\Component\Security\Http\Firewall\ListenerInterface;
    use Symfony\Component\Security\Core\Exception\AuthenticationException;
    use Symfony\Component\Security\Core\SecurityContextInterface;
    use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
    use Symfony\Component\Security\Http\HttpUtils;
     
     
    use Acme\SecurityBundle\Security\Authentication\Token\UserToken;
     
    // Lorsque l'on veut un formulaire de login, il faut hériter de AbstractAuthenticationListener
    // L'implémentation de ListenerInterface ne convient pas dans ce cas.
    class AuthListener extends AbstractAuthenticationListener
    {
        protected $securityContext;
        protected $authenticationManager;
        protected $httpUtils;
     
        public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager,
                                    SessionAuthenticationStrategyInterface $sessionStrategy, HttpUtils $httpUtils, $options = array())
        {
            parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $httpUtils, "user", array_merge(array(
                'username_parameter' => '_username',
                'password_parameter' => '_password',
                'intention' => 'authenticate',
                'post_only' => true,
            ), $options));
        }
     
        /**
         * Performs authentication.
         *
         * @param  Request $request A Request instance
         *
         * @return TokenInterface The authenticated token, or null if full authentication is not possible
         *
         * @throws AuthenticationException if the authentication fails
         */
        protected function attemptAuthentication(Request $request)
        {
     
            $username = trim($request->get($this->options['username_parameter'], null, true));
            $password = $request->get($this->options['password_parameter'], null, true);
     
            //$request->getSession()->set(SecurityContextInterface::LAST_USERNAME, $username);
     
            return $this->authenticationManager->authenticate(new UserToken($username, $password, $this->providerKey));
     
        }
     
        public function getHttpUtils()
        {
            return $this->httpUtils;
        }
     
        public function setHttpUtils($httpUtils)
        {
            $this->httpUtils = $httpUtils;
        }
    }
    L'authenticationProvider :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
     
    <?php
    // Acme/SecurityBundle/Security/Authentication/Provider/AuthProvider.php
     
    namespace Acme\SecurityBundle\Security\Authentication\Provider;
     
    use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
    use Symfony\Component\Security\Core\User\UserProviderInterface;
    use Symfony\Component\Security\Core\Exception\AuthenticationException;
    use Symfony\Component\Security\Core\Exception\NonceExpiredException;
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Symfony\Component\Security\Core\Exception\BadCredentialsException;
     
    use Acme\SecurityBundle\Security\Authentication\Token\UserToken;
     
    class AuthProvider implements AuthenticationProviderInterface
    {
        private $userProvider;
        private $cacheDir;
     
        public function __construct(UserProviderInterface $userProvider, $cacheDir)
        {
            $this->userProvider = $userProvider;
            $this->cacheDir     = $cacheDir;
        }
     
        public function authenticate(TokenInterface $token)
        {
            $user = $this->userProvider->loadUserByUsername($token->getUsername());
            // $userToken = new UserToken();
            // $userToken->setUser($user);
            // echo "it worked"; exit;
            $newToken = new UserToken($token->getUser(), $token->getCredentials(), "user", array("ROLE_ADMIN"));
            $username = $newToken->getUser();
            if (empty($username)) {
                throw new BadCredentialsException('Bad credentials :)');
            }
            //return $newToken;
     
            if ($user && $this->validate()) {
                $authenticatedToken = new UserToken($token->getUser(), $token->getCredentials(), "user", $user->getRoles());
                $authenticatedToken->setUser($user);
     
                return $authenticatedToken;
            }
        }
     
        public function supports(TokenInterface $token)
        {
            return $token instanceof UserToken;
        }
     
        public function validate()
        {
            return true;
        }
     
    }
    Le UserToken :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
     
    <?php
    // Acme/SecurityBundle/Security/Authenticaion/Token/UserToken.php
     
    namespace Acme\SecurityBundle\Security\Authentication\Token;
     
    use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
     
    /**
     * UsernamePasswordToken implements a username and password token.
     *
     */
     
    class UserToken extends AbstractToken
    {
        private $credentials;
        private $providerKey;
     
        /**
         * Constructor.
         *
         * @param string $user        The username (like a nickname, email address, etc.)
         * @param string $credentials This usually is the password of the user
         * @param string $providerKey The provider key
         * @param array  $roles       An array of roles
         *
         * @throws \InvalidArgumentException
         */
        public function __construct($user, $credentials, $providerKey, array $roles = array())
        {
            parent::__construct($roles);
     
            if (empty($providerKey)) {
                throw new \InvalidArgumentException('$providerKey must not be empty.');
            }
     
            $this->setUser($user);
            $this->credentials = $credentials;
            $this->providerKey = $providerKey;
     
            parent::setAuthenticated(count($roles) > 0);
        }
     
        /**
         * {@inheritdoc}
         */
        public function setAuthenticated($isAuthenticated)
        {
            if ($isAuthenticated) {
                throw new \LogicException('Cannot set this token to trusted after instantiation.');
            }
     
            parent::setAuthenticated(false);
        }
     
        public function getCredentials()
        {
            return $this->credentials;
        }
     
        public function getProviderKey()
        {
            return $this->providerKey;
        }
     
        /**
         * {@inheritdoc}
         */
        public function eraseCredentials()
        {
            parent::eraseCredentials();
     
            $this->credentials = null;
        }
     
        public function serialize()
        {
            return serialize(array($this->credentials, $this->providerKey, parent::serialize()));
        }
     
        public function unserialize($str)
        {
            list($this->credentials, $this->providerKey, $parentStr) = unserialize($str);
            parent::unserialize($parentStr);
        }
    }
    Le User Provider :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
     
    <?php
    // src/Acme/SecurityBundle/Security/User/UserProvider.php
     
    namespace Acme\SecurityBundle\Security\User;
     
    use Symfony\Component\Security\Core\User\UserProviderInterface;
    use Symfony\Component\Security\Core\User\UserInterface;
    use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
    use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
     
    use Acme\SecurityBundle\Security\User\User;
     
    class UserProvider implements UserProviderInterface
    {
        public function loadUserByUsername($username)
        {
            // make a call to your webservice here
            // $userData = ...
            // pretend it returns an array on success, false if there is no user
            $user = new User();
            $user->setUsername($username);
            $user->setPassword("1234");
            $user->setRoles(array("ROLE_ADMIN"));
     
            return $user;
     
    //        if ($userData) {
    //            // $password = '...';
    //            // ...
    //
    //            return new WebserviceUser($username, $password, $salt, $roles)
    //        } else {
    //            throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
    //        }
        }
     
        public function refreshUser(UserInterface $user)
        {
            if (!$user instanceof User) {
                throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
            }
     
            return $this->loadUserByUsername($user->getUsername());
        }
     
        public function supportsClass($class)
        {
            return $class === 'Acme\SecurityBundle\Security\User';
        }
    }
    Le User :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
     
    <?php
    // src/Acme/SecurityBundle/Security/User/User.php
     
    namespace Acme\SecurityBundle\Security\User;
     
    use Symfony\Component\Security\Core\User\UserInterface;
     
    class User implements UserInterface
    {
        private $username;
        private $password;
        private $salt;
        private $roles;
     
        public function getRoles()
        {
            return $this->roles;
        }
     
        public function getPassword()
        {
            return $this->password;
        }
     
        public function getSalt()
        {
            return $this->salt;
        }
     
        public function getUsername()
        {
            return $this->username;
        }
     
        public function setRoles($roles)
        {
            $this->roles = $roles;
        }
     
        public function setPassword($password)
        {
            $this->password = $password;
        }
     
        public function setSalt($salt)
        {
            $this->salt = $salt;
        }
     
        public function setUsername($username)
        {
            $this->username = $username;
        }
     
        public function eraseCredentials()
        {
        }
     
        public function equals(UserInterface $user)
        {
            if (!$user instanceof User) {
                return false;
            }
            if ($this->password !== $user->getPassword()) {
                return false;
            }
            if ($this->getSalt() !== $user->getSalt()) {
                return false;
            }
            if ($this->username !== $user->getUsername()) {
                return false;
            }
            return true;
        }
    }
    Le paramétrage

    Le routing du bundle :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    # Acme/SecurityBundle/Resources/config/routing.yml
    AcmeSecurityBundle_homepage:
        pattern:  /
        defaults: { _controller: AcmeSecurityBundle:Default:index }
     
    login:
        pattern: /login
        defaults: { _controller: AcmeSecurityBundle:Security:login }
     
    login_check:
        pattern: /login_check
    La déclaration de la factory

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    # Acme/SecurityBundle/Resources/config/security_factories.yml
     
    services:
        security.authentication.factory.user:
            class:  Acme\SecurityBundle\DependencyInjection\Security\Factory\UserFactory
            tags:
                - { name: security.listener.factory }
    La déclaration des services :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
     
    # Acme/SecurityBundle/Resources/config/services.yml
     
    services:
        user.security.authentication.provider:
            class:  Acme\SecurityBundle\Security\Authentication\Provider\AuthProvider
            arguments: ["", %kernel.cache_dir%/security/nonces]
     
        user.security.authentication.listener:
            class:  Acme\SecurityBundle\Security\Authentication\Firewall\AuthListener
            arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils]
            tags:
                - { name: monolog.logger, channel: security }
     
        user_provider_service:
            class: Acme\SecurityBundle\Security\User\UserProvider
    Le paramétrage au niveau de l'application :

    Le routing général

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    # /app/config/routing.yml
     
    AcmeSecurityBundle:
        resource: "@AcmeSecurityBundle/Resources/config/routing.yml"
        prefix:   /
    Security...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
     
    # app/config/security.yml
    security:
        factories:
            - "%kernel.root_dir%/../src/Acme/SecurityBundle/Resources/config/security_factories.yml"
     
        firewalls:
            login:
                pattern:  ^/login$
                security: false
     
            checkpoint:
                pattern: ^/
                my_user_factory: true  # Correspond à la valeur renvoyée par la fonction getKey de la factory que je souhaite utiliser
                form_login:
                    login_path: /login
                    check_path: /login_check
                logout:
                    path:   /logout
                    target: /
     
        encoders:
            Acme\SecurityBundle\Entity\User: sha512
     
        role_hierarchy:
            ROLE_ADMIN:       ROLE_USER
            ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
     
        providers:
             user_provider:
                 id: user_provider_service # défini dans le service.yml du bundle
     
        access_control:
    #        - { path: ^/_internal, role: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
    #        - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
    #        - { path: ^/event/new, roles: ROLE_ADMIN }
    #        - { path: ^/hello, roles: ROLE_USER }
    Les contrôleurs et les templates

    Le contrôleur de la page d'accueil...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    <?php
    // Acme/SecurityBundle/Controller/DefaultController.php
     
    namespace Acme\SecurityBundle\Controller;
     
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
     
     
    class DefaultController extends Controller
    {
     
        public function indexAction()
        {
            // Je profite de l'occasion pour regarder le contexte... (à ne pas faire en prod :-) )
            return $this->render('AcmeSecurityBundle:Default:index.html.twig', 
                      array('parametres' => print_r($this->container->getParameterBag()->all(), true),
                            'request' => print_r($this->getRequest(),true)));
        }
    }
    et le template associé

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    {# Acme/SecurityBundle/Resources/views/Default/index.html.twig #}
    Hello !
     
    <pre>
    Parametres
    {{ parametres }}
    Request
    {{ request }}
    </pre>
    Le contrôleur de la page de login

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
     
    <?php
    // src/Acme/SecurityBundle/Controller/SecurityController.php
     
    namespace Acme\SecurityBundle\Controller;
     
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Symfony\Component\Security\Core\SecurityContext;
     
    class SecurityController extends Controller
    {
        public function loginAction()
        {
            $request = $this->getRequest();
            $session = $request->getSession();
     
            // get the login error if there is one
            $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
            $session->remove(SecurityContext::AUTHENTICATION_ERROR);
     
            return $this->render('AcmeSecurityBundle:Login:login.html.twig', array(
                // last username entered by the user
                'last_username' => $session->get(SecurityContext::LAST_USERNAME),
                'error'         => $error,
            ));
        }
     
    }
    et le template associé

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    {# Acme/SecurityBundle/Resources/views/Security/login.html.twig #}
    {% if error %}
        <div>{{ error.message }}</div>
    {% endif %}
    <form name="loginForm" action="{{ path('login_check') }}" method="post">
        <label for="username">Username:</label>
        <input type="text" id="username" name="_username" />
        <label for="password">Password:</label>
        <input type="password" id="password" name="_password" />
        {#
            If you want to control the URL the user is redirected to on success (more details
            below)
            <input type="hidden" name="_target_path" value="/account" />
        #}
        <button type="submit">login</button>
    </form>
    </script -->
    Voilà. Je pense avoir fait le tour et j'espère que tout ça est suffisamment clair...

    Amusez-vous bien :-)

  2. #2
    Membre éprouvé Avatar de anta_res
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mai 2006
    Messages
    93
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2006
    Messages : 93
    Par défaut
    Et bien chapeau bas Mr fatbob.

    Je pense que ce post va aider un paquet de monde vu la galère que c'est de mettre en place un système d'authentification personnalisé.
    J'ai pas lu tout le code à fond mais ça m'a l'air propre et je pense que ça fait déjà un bon point départ pour tout ceux qui comme nous vont sortir les rames pour essayer de créer leur propre authentification.

  3. #3
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    Merci.
    N'hésite pas à corriger, ou préciser des éléments (en particulier pour clarifier certaines interactions). Je suis assez nouveau avec symfony et c'est un peu mon baptême du feu... J'ai sans doute oublié des trucs.

  4. #4
    Membre averti
    Homme Profil pro
    Développeur Web
    Inscrit en
    Mai 2012
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Mai 2012
    Messages : 21
    Par défaut Mon
    Bonjour,

    Vraiment merci pour ta super doc.

    Par contre, pour que cela marche correctement, j'ai du mettre parent::setAuthenticated(true); au lieu de parent::setAuthenticated(false);

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
         // Fichier AuthProvider.php
         public function setAuthenticated($isAuthenticated)
        {
            if ($isAuthenticated) {
                throw new \LogicException('Cannot set this token to trusted after instantiation.');
            }
     
            parent::setAuthenticated(true);
        }
    Si je laisse à false dans mon profiler j'ai : "username" (no auth.) et je ne peux pas faire mes tests dans mon controleur pour voir si l'utilisateur a bien l'autorisation

  5. #5
    Rédacteur
    Avatar de Viduc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2009
    Messages
    1 445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Service public

    Informations forums :
    Inscription : Février 2009
    Messages : 1 445
    Billets dans le blog
    2
    Par défaut
    Salut et félicitation également!

    une petite correction à apporter dans ton securitycontroller tu as mis:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     return $this->render('AcmeSecurityBundle:Login:login.html.twig', array(
    alors que ce doit être

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     return $this->render('AcmeSecurityBundle:Security:login.html.twig', array(
    C'est coole je vais enfin avoir une bonne base pour avancer!!! encore merci à toi

  6. #6
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    @Viduc : Exact... Malheureusement, je ne peux pas éditer mon post...
    @ray-k : c'est étrange. Chez moi ça fonctionne bien comme ça. Mettre true dans le parent::setAuthenticated me semble curieux dans la mesure où juste avant, on dit : "Si le paramètre est à true, balancer l'exception comme quoi on n'authentifie pas ce token après sa création", or c'est ce que tu fais en mettant true...

  7. #7
    Nouveau candidat au Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2012
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Septembre 2012
    Messages : 2
    Par défaut
    Bonjour,

    Tout d'abord merci pour ce post qui m'aide grandement.
    Je rencontre un problème dont je n'ai pas trouvé la solution pour le moment.

    Voilà ce que j'ai :

    InvalidConfigurationException: Unrecognized options "my_user_factory" under "security.firewalls.checkpoint"

    je ne sais pas comment règler ce problème, si quelqu'un a une idée, je suis là.

  8. #8
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    my_user_factory est la valeur renvoyée par la fonction getKey de la Factory, qui elle même, est déclarée dans
    # Acme/SecurityBundle/Resources/config/security_factories.yml
    Est-ce bien paramétré dans ton application ?

  9. #9
    Nouveau candidat au Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2012
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Septembre 2012
    Messages : 2
    Par défaut
    Citation Envoyé par fatbob Voir le message
    my_user_factory est la valeur renvoyée par la fonction getKey de la Factory, qui elle même, est déclarée dans
    # Acme/SecurityBundle/Resources/config/security_factories.yml
    Est-ce bien paramétré dans ton application ?
    Je ne sais pas s'il y a un rapport, mais quelle est la version de symfony à utiliser ? Moi j'ai pris la version 2.1.

    Peut être que cela ne fonctionne pas avec la 2.1.

  10. #10
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    C'est vrai que ma version est une 2.0... Je ne sais pas si mon code fonctionne sur une 2.1.

  11. #11
    Rédacteur
    Avatar de Viduc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2009
    Messages
    1 445
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Service public

    Informations forums :
    Inscription : Février 2009
    Messages : 1 445
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par fatbob Voir le message
    @Viduc : Exact... Malheureusement, je ne peux pas éditer mon post...
    @ray-k : c'est étrange. Chez moi ça fonctionne bien comme ça. Mettre true dans le parent::setAuthenticated me semble curieux dans la mesure où juste avant, on dit : "Si le paramètre est à true, balancer l'exception comme quoi on n'authentifie pas ce token après sa création", or c'est ce que tu fais en mettant true...
    oui moi j'ai laissé à false et ça fonctionne

  12. #12
    Membre averti
    Homme Profil pro
    Consultant technico-fonctionnel
    Inscrit en
    Juin 2012
    Messages
    55
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant technico-fonctionnel
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2012
    Messages : 55
    Par défaut
    bonjour, je vien de reprendre extactement ce que tu as fais de zero et cette erreur aparait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    InvalidConfigurationException: Unrecognized options "my_user_factory" under "security.firewalls.checkpoint"
    je ne comprend pas pourquoi.. :/
    je suis sous symfony 2.1.6

  13. #13
    Membre émérite
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Août 2011
    Messages
    477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Août 2011
    Messages : 477
    Par défaut
    Tu utilises un tuto écris pour la version 2.0

    Voir dans ce lien http://symfony.com/doc/master/cookbo..._provider.html les modifications pour la version 2.1 au niveau des factories

  14. #14
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut Adaptation à la version 2.1 de Symfony
    Adaptation à Symfony 2.1

    En premier lieu, la définition du service n'est plus faite dans le fichier factories
    Acme\SecurityBundle\Resources\config\security_factories.yml. Le fichier peut être supprimé.
    Les services sont maintenant chargés automatiquement par la fonction build du bundle
    SecurityBundle/AcmeSecurityBundle.php


    Modification du paramétrage security.yml

    Suppression du paramètre factories
    Modification du provider : Le paramètrage à changé (ajout du chain_provider).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    # AppConfig\security.yml
    security:
       encoders:
          Symfony\Component\Security\Core\User\User: plaintext
     
    providers:
         user_provider:
             id: user_provider_service
         chain_provider:
             chain:
                 providers: user_provider
     firewalls:
         login:
             pattern:  ^/login$
             security: false
     
         logout:
             pattern:  ^/(logout$)
             security: false
     
         secured_area:
             pattern: ^/
             access_master_factory:
                 login_path: /login
                 check_path: /login_check
                 failure_path: /logouterreur

    Chargement de la factory

    Création de la fonction build, qui charge la configuration de la sécurité et instancie le listener.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
     
    <?php
    // Acme\SecurityBundle\AcmeSecurityBundle.php
    namespace Acme\SecurityBundle;
     
    use Symfony\Component\HttpKernel\Bundle\Bundle;
    use Symfony\Component\DependencyInjection\ContainerBuilder;
    use Acme\SecurityBundle\DependencyInjection\Security\Factory\AcmeFactory;
     
     
     
    class AcmeSecurityBundle extends Bundle
    {
       public function build(ContainerBuilder $container)
       {
     
          parent::build($container);
     
          $extension = $container->getExtension('security');
          $extension->addSecurityListenerFactory(new AcmeFactory());
     
       }
    }
    ?>
    Initialisation du Listener et de l’authenticationProvider

    Modification du service.yml

    Ajout du paramètre @kernel au listener Acme.security.authentication.listener ( cf.
    Acme\SecurityBundle\Security\Firewall\AcmeAuthenticationListener.php)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    # Acme\SecurityBundle\Resources\config\services.yml
    #...
    services:
       #...
       Acme.security.authentication.listener:
          class:  Acme\SecurityBundle\Security\Authentication\Firewall\AcmeAuthenticationListener
          arguments: [@security.context, @security.authentication.manager, @security.authentication.session_strategy, @security.http_utils, @logger, "", @kernel]
          tags:
              - { name: monolog.logger, channel: security }
     
     #...

    Modificaiton du listener

    Le passage de Sf 2.1 à 2.0 contient des modification dans l’interface AbstractAuthenticationListener :

    L’ordre des paramètres est différent :
    . succesHandler et failureHandler se trouvent avant $option.
    . succesHandler et failureHandler sont OBLIGATIORES

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    <?php
    // Acme\SecurityBundle\Security\Firewall\AcmeAuthenticationListener.php
    // Sf 2.1
           public function __construct(
                 SecurityContextInterface $securityContext,
                 AuthenticationManagerInterface $authenticationManager,
                 SessionAuthenticationStrategyInterface $sessionStrategy,
                 HttpUtils $httpUtils,
                 $providerKey,
                 AuthenticationSuccessHandlerInterface $successHandler,
                 AuthenticationFailureHandlerInterface $failureHandler,
                 array $options = array(),
                 LoggerInterface $logger = null,
                 EventDispatcherInterface $dispatcher = null)
     
    //Sf 2.0
           public function __construct(
                 SecurityContextInterface $securityContext,
                 AuthenticationManagerInterface $authenticationManager,
                 SessionAuthenticationStrategyInterface $sessionStrategy,
                 HttpUtils $httpUtils,
                 $providerKey,
                 array $options = array(),
                 AuthenticationSuccessHandlerInterface $successHandler = null,
                 AuthenticationFailureHandlerInterface $failureHandler = null,
                 LoggerInterface $logger = null,
                 EventDispatcherInterface $dispatcher = null)
     ?>
    La classe a donc été modifiée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
     
    <?php
    // Acme\SecurityBundle\Security\Firewall\AcmeAuthenticationListener.php
    // Ajout dépendances.
    use Symfony\Component\HttpKernel\HttpKernel;
     
    use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
     
    use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
     
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\HttpKernel\Event\GetResponseEvent;
    use Symfony\Component\Security\Http\Firewall\ListenerInterface;
    use Symfony\Component\Security\Core\Exception\AuthenticationException;
    use Symfony\Component\Security\Core\Exception\ProviderNotFoundException;
    use Symfony\Component\Security\Core\SecurityContextInterface;
    use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Symfony\Component\Security\Http\Firewall\AbstractAuthenticationListener;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
    use Symfony\Component\Security\Http\HttpUtils;
     
     
    use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
    use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
    use Symfony\Component\HttpKernel\HttpKernelInterface;
    // Possibilité de passer par des Handlers si besoin ...
    //use Acme\SecurityBundle\Security\Authentication\Handler\LoginSuccessHandler;
    //use Acme\SecurityBundle\Security\Authentication\Handler\LoginFailureHandler;
     
     
    use Acme\SecurityBundle\Security\Authentication\Token\AcmeToken;
     
    // mise à jour du contruct
    public function __construct(SecurityContextInterface $securityContext,
       AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy,
       HttpUtils $httpUtils, $logger, $options = array(), $httpKernel)
     
       // appel au contructeur parent
     
       parent::__construct( $securityContext, $authenticationManager, $sessionStrategy, $httpUtils, "Acme",
                new DefaultAuthenticationSuccessHandler($httpUtils, $options),
                new DefaultAuthenticationFailureHandler($httpKernel, $httpUtils, $options),
                array_merge(array('username_parameter' => '_username',
                            'password_parameter' => '_password',
                            'intention' => 'authenticate',
                            'post_only' => true,
                            ),
             $options)
     
          );
    ?>
    Modification (légère) du User

    Modification du nom de la fonction equals :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // Acme\SecurityBundle\Security\User\AcmeUser.php
    public function equals(UserInterface $user)
    // devient
    public function isEqualTo(UserInterface $user)

  15. #15
    Membre averti
    Homme Profil pro
    Consultant technico-fonctionnel
    Inscrit en
    Juin 2012
    Messages
    55
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant technico-fonctionnel
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2012
    Messages : 55
    Par défaut
    bonjour, a quoi correspond : dans le fichier service.yml de la version 2.1?

  16. #16
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    Citation Envoyé par choco7 Voir le message
    bonjour, a quoi correspond : dans le fichier service.yml de la version 2.1?
    A rien.
    C'est le paramétrage dans mon appli que je passe par là.
    Je l'enlève de mon précédent post pour éviter les confusions inutiles.

  17. #17
    Membre émérite
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Août 2011
    Messages
    477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France

    Informations professionnelles :
    Activité : Technicien maintenance
    Secteur : Administration - Collectivité locale

    Informations forums :
    Inscription : Août 2011
    Messages : 477
    Par défaut
    Je comprends mieux la question, ça manquait

  18. #18
    Membre averti
    Homme Profil pro
    Consultant technico-fonctionnel
    Inscrit en
    Juin 2012
    Messages
    55
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant technico-fonctionnel
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2012
    Messages : 55
    Par défaut
    je me retrouve avec une erreur: il manque je pense les use pour :

    DefaultAuthenticationSuccessHandler et DefaultAuthenticationFailureHandler

  19. #19
    Membre éclairé

    Inscrit en
    Novembre 2008
    Messages
    423
    Détails du profil
    Informations forums :
    Inscription : Novembre 2008
    Messages : 423
    Par défaut
    Citation Envoyé par choco7 Voir le message
    je me retrouve avec une erreur: il manque je pense les use pour :

    DefaultAuthenticationSuccessHandler et DefaultAuthenticationFailureHandler
    Corrigé.
    J'ai mis tous les use que j'ai.

  20. #20
    Membre averti
    Homme Profil pro
    Consultant technico-fonctionnel
    Inscrit en
    Juin 2012
    Messages
    55
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Consultant technico-fonctionnel
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2012
    Messages : 55
    Par défaut
    ca marche de ton cotés? moi j’obtiens cette erreur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Catchable Fatal Error: Argument 2 passed to Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler::__construct() must be an array, string given, called in C:\wamp\www\Symfony\src\Developpement\CartopliBundle\Security\Firewall\AuthListener.php on line 42 and defined in C:\wamp\www\Symfony\vendor\symfony\symfony\src\Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler.php line 40
    :/

Discussions similaires

  1. Quelle solution complète PME: ADSL, Parfeu, VPN et Backup
    Par geekforever dans le forum Hardware
    Réponses: 1
    Dernier message: 23/02/2011, 22h52
  2. Réponses: 0
    Dernier message: 22/09/2010, 22h52
  3. Réponses: 0
    Dernier message: 21/06/2010, 13h49
  4. Recherche de solution complète de mise en marché
    Par Vol dans le forum E-Commerce
    Réponses: 2
    Dernier message: 04/01/2009, 05h53
  5. Assistant d'import d'une solution complète
    Par SaumonAgile dans le forum Dreamshield
    Réponses: 3
    Dernier message: 03/09/2007, 23h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo