Securiser un backoffice et un frontoffice avec des providers différents

De manière générale, un backoffice se sécurise rapidement avec FosUserBundle, et c’est très bien ainsi, car on ne veut pas s’embêter à recoder tout un système d’authentification classique (login/rappel de mot de passe, changement de mot de passe etc)

Mais, de manière générale, côté front, il est courant de vouloir à la fois avoir une table différente de FosUser, et avoir une authentification complètement personnalisée (si vous utilisez FosUserBundle, il faudra surcharger les contrôleurs pour pouvoir faire ce que vous voulez)

Dans une série de précédents articles, nous avons vus comment créer une authentification de A à Z sans FosUserBundle.

J’ai repris les sources dans un nouveau projet (https://github.com/jpsymfony/REST-BEHAT)  en combinant FosUserBundle pour le backoffice et une authentification personnalisée pour le front.

Comment mettre en place les providers et firewalls?

Comme à l’accoutumée lorsqu’on veut hériter d’un bundle, j’en ai créé un que j’ai appelé BackUserBundle, et qui étend FosUserBundle.

Dans le CoreBundle, j’ai placé les entités et les repositories. Les formTypes se trouvent dans les bundles où ils sont appelés (ici BlogBundle)

Voici comment se présente le fichier security.yml:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: sha512
        App\CoreBundle\Entity\User: sha512

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username
        database_users:
            entity: { class: AppCoreBundle:User, property: username }

    firewalls:
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false
        bo:
            pattern: ^/admin
            form_login:
                provider: fos_userbundle
                csrf_provider: form.csrf_provider
                login_path:  /admin/login
                check_path:  /admin/login_check
            logout:
                path:        /admin/logout
                target:      /
            anonymous:    ~
            remember_me:
                key:      "%secret%"
                lifetime: 31536000 # 365 jours en secondes
                path:     /
                domain:   ~ # Prend la valeur par défaut du domaine courant depuis $_SERVER
        fo:
            # this firewall applies to all URLs
            pattern: ^/
 
            # but the firewall does not require login on every page
            # denying access is done in access_control or in your controllers
            anonymous: true
            
            # This allows the user to login by submitting a username and password
            # Reference: http://symfony.com/doc/current/cookbook/security/form_login_setup.html
            form_login:
                provider: database_users
                # The route name that the login form submits to
                check_path: security_login_check
                # The name of the route where the login form lives
                # When the user tries to access a protected page, they are redirected here
                login_path: security_login_form
                # Secure the login form against CSRF
                # Reference: http://symfony.com/doc/current/cookbook/security/csrf_in_login_form.html
                csrf_provider: security.csrf.token_manager
                
                default_target_path: user_dashboard
                always_use_default_target_path: true
            logout:
                # The route name the user can go to in order to logout
                path: security_logout
                # The name of the route to redirect to after logging out
                target: homepage

    access_control:
        - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/admin, role: ROLE_ADMIN }
        - { path: ^/account/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/account, roles: IS_AUTHENTICATED_FULLY }
        - { path: ^/, role: IS_AUTHENTICATED_ANONYMOUSLY }

Comme vous pouvez le voir, il y a deux encoders, un faisant appel à Fos, l’autre étant ma classe de User.

Concernant les providers, je déclare les deux.

Côté firewalls, j’ai l’habituel dev, puis le backoffice, et enfin le frontoffice. Petite subtilité, remarquez que pour chaque firewall, j’ai indiqué quel provider était utilisé, sinon Symfony ne sait pas lequel choisir et prend le premier qu’on lui donne (ici celui de Fos, ce qui revient à être refoulé à l’authentification)

Et enfin, côté access_control, uniquement du classique, puisque je sécurise  l’interface admin, sécurise l’espace authentifié d’un utilisateur et autorise le reste en anonymous.

Si vous désirez tester l’application, n’oubliez pas de lancer la commande php app/console doctrine:fixtures:load pour avoir des users en base (cf le détail des fixtures dans CoreBundle/DataFixtures)

Rédigé par

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.