8
votes

Formulaire de connexion Symfony 4: authentification réussie, mais authentification immédiatement perdue après la redirection

J'ai construit un formulaire de connexion en suivant ce document de configuration de connexion .

Cela fonctionne bien sur localhost mais pas sur le serveur de production .

Sur localhost et prod, l'authentification commence avec succès

  1. Authentification Guard réussie
  2. Réponse de réussite de l'ensemble d'authentificateurs de garde
  3. Stocké le jeton de sécurité dans la session
  4. Itinéraire correspondant "easyadmin

    server { # this block only redirects www to non www
            listen aaa.bbb.ccc.ddd:443 ssl;
            server_name www.somewebsite.com;
    
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            ssl_certificate /var/www/clients/client0/web4/ssl/somewebsite.com-le.crt;
            ssl_certificate_key /var/www/clients/client0/web4/ssl/somewebsite.com-le.key;
    
            return 301 https://somewebsite.com$request_uri;
    }
    
    server { # this block redirects ssl requests to Varnish
            listen aaa.bbb.ccc.ddd:443 ssl;
            server_name somewebsite.com;
    
            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
            ssl_certificate /var/www/clients/client0/web4/ssl/somewebsite.com-le.crt;
            ssl_certificate_key /var/www/clients/client0/web4/ssl/somewebsite.com-le.key;
    
            location / {
                # Pass the request on to Varnish.
                proxy_pass  http://127.0.0.1;
    
                # Pass some headers to the downstream server, so it can identify the host.
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
                # Tell any web apps that the session is HTTPS.
                proxy_set_header X-Forwarded-Proto https;
                proxy_redirect     off;
            }
    }
    
    server { # now sent to backend 
            listen aaa.bbb.ccc.ddd:8083;
            server_name somewebsite.com;
            root   /var/www/somewebsite.com/web/public;
    
            location / {
                try_files $uri /index.php$is_args$args;
           }
           location ~ ^/index\.php(/|$) {
                fastcgi_pass 127.0.0.1:8998;
    
                fastcgi_split_path_info ^(.+\.php)(/.*)$;
                include fastcgi_params;
    
                fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
                fastcgi_param DOCUMENT_ROOT $realpath_root;
    
                internal;
            }
            location ~ \.php$ {
                return 404;
            }
    
            error_log /var/log/ispconfig/httpd/somewebsite.com/error.log;
            access_log /var/log/ispconfig/httpd/somewebsite.com/access.log combined;
    
            location ~ /\. {
                            deny all;
            }
            location ^~ /.well-known/acme-challenge/ {
                            access_log off;
                            log_not_found off;
                            root /usr/local/ispconfig/interface/acme/;
                            autoindex off;
                            try_files $uri $uri/ =404;
            }
            location = /favicon.ico {
                log_not_found off;
                access_log off;
                expires max;
            }
            location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
            }
    }
    

Mais en localhost, je suis correctement redirigé vers le backoffice:

  • Lire le jeton de sécurité existant de la session
  • L'utilisateur a été rechargé à partir d'un fournisseur d'utilisateurs

    namespace App\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    // use Symfony\Component\Security\Core\User\EquatableInterface;
    use Symfony\Component\Security\Core\User\UserInterface;
    
    /**
     * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
     */
    class User implements UserInterface # , EquatableInterface
    {
        /**
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         */
        private $id;
    
        /**
         * @ORM\Column(type="string", length=180, unique=true)
         */
        private $email;
    
        /**
         * @ORM\Column(type="json")
         */
        private $roles = [];
    
        /**
         * @var string The hashed password
         * @ORM\Column(type="string")
         */
        private $password;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $name;
    
        /**
         * @var string le token qui servira lors de l'oubli de mot de passe
         * @ORM\Column(type="string", length=255, nullable=true)
         */
        protected $resetToken;
    
      /*public function __construct($username, $password, array $roles)
      {
        $this->username = $username;
        $this->password = $password;
        $this->roles = $roles;
      }*/
    
    
        public function getId(): ?int
        {
            return $this->id;
        }
    
        public function getEmail(): ?string
        {
            return $this->email;
        }
    
        public function setEmail(string $email): self
        {
            $this->email = $email;
    
            return $this;
        }
    
        /**
         * A visual identifier that represents this user.
         *
         * @see UserInterface
         */
        public function getUsername(): string
        {
            return (string) $this->email;
        }
    
        /**
         * @see UserInterface
         */
        public function getRoles(): array
        {
            $roles = $this->roles;
            // guarantee every user at least has ROLE_USER
            $roles[] = 'ROLE_USER';
    
            return array_unique($roles);
        }
    
        public function setRoles(array $roles): self
        {
            $this->roles = $roles;
    
            return $this;
        }
    
        /**
         * @see UserInterface
         */
        public function getPassword(): string
        {
            return (string) $this->password;
        }
    
        public function setPassword(string $password): self
        {
            $this->password = $password;
    
            return $this;
        }
    
        /**
         * @see UserInterface
         */
        public function getSalt()
        {
            // not needed when using the "bcrypt" algorithm in security.yaml
        }
    
        /**
         * @see UserInterface
         */
        public function eraseCredentials()
        {
            // If you store any temporary, sensitive data on the user, clear it here
            // $this->plainPassword = null;
        }
    
        public function getName(): ?string
        {
            return $this->name;
        }
    
        public function setName(string $name): self
        {
            $this->name = $name;
    
            return $this;
        }
    
        /**
         * @return string
         */
        public function getResetToken(): string
        {
          return $this->resetToken;
        }
    
        /**
         * @param string $resetToken
         */
        public function setResetToken(?string $resetToken): void
        {
          $this->resetToken = $resetToken;
        }
    
        public function __toString() {
          return $this->getName() ;
        }
    
    /*    public function isEqualTo(UserInterface $user)
        {
    
          if ($this->password !== $user->getPassword()) {
            return false;
          }
    
    
          if ($this->email !== $user->getUsername()) {
            return false;
          }
    
          return true;
        }*/
    }
    

Dans l'environnement de production, à la place:

  • il saute l'étape: lecture du jeton de sécurité existant
  • n'actualise pas l' utilisateur comme prévu
  • à la place, il remplit le TokenStorage avec un Token anonyme
  • Accès refusé et retour à l'URL de connexion

    # 1st one shows user logged in with correct roles and so on
    _sf2_attributes|a:3:{s:24:"_csrf/https-authenticate";s:43:"erWMU-irtptcZodr8UOjFtxiuyE23LbAeFHRnXgcNdc";s:23:"_security.last_username";s:21:"user_email@gmail.com";s:14:"_security_main";s:799:"C:67:"Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken":718:{a:2:{i:0;s:4:"main";i:1;a:5:{i:0;O:15:"App\Entity\User":6:{s:19:"^@App\Entity\User^@id";i:1;s:22:"^@App\Entity\User^@email";s:21:"user_email@gmail.com";s:22:"^@App\Entity\User^@roles";a:1:{i:0;s:11:"ROLE_EDITOR";}s:25:"^@App\Entity\User^@password";s:60:"$2y$13$cXaR7Ss.kTH1U.T/Rzi6m.ALsKwWCLDcO5/OIeRDAq02iylmf4us6";s:21:"^@App\Entity\User^@name";s:7:"Thierry";s:13:"^@*^@resetToken";N;}i:1;b:1;i:2;a:2:{i:0;O:41:"Symfony\Component\Security\Core\Role\Role":1:{s:47:"^@Symfony\Component\Security\Core\Role\Role^@role";s:11:"ROLE_EDITOR";}i:1;O:41:"Symfony\Component\Security\Core\Role\Role":1:{s:47:"^@Symfony\Component\Security\Core\Role\Role^@role";s:9:"ROLE_USER";}}i:3;a:0:{}i:4;a:2:{i:0;s:11:"ROLE_EDITOR";i:1;s:9:"ROLE_USER";}}}}";}_sf2_meta|a:3:{s:1:"u";i:1562668713;s:1:"c";i:1562668713;s:1:"l";s:1:"0";}
    
    # 2nd one ...is empty
    
    # 3rd one refers to backoffice url
    _sf2_attributes|a:1:{s:26:"_security.main.target_path";s:42:"https://mywebsite.com/backoffice";}_sf2_meta|a:3:{s:1:"u";i:1562668713;s:1:"c";i:1562668713;s:1:"l";s:1:"0";}
    
    # last one is similar to point 3, before logging, only ssid value differs, and a corresponding cookie is set on Chrome
    _sf2_attributes|a:1:{s:24:"_csrf/https-authenticate";s:43:"3UC5dCRrahc2qhdZ167Jg4HKTJCexf8PFlefibTVpYk";}_sf2_meta|a:3:{s:1:"u";i:1562668713;s:1:"c";i:1562668713;s:1:"l";s:1:"0";}
    

security.yaml

_sf2_attributes|a:1:{s:24:"_csrf/https-authenticate";s:43:"erWMU-irtptcZodr8UOjFtxiuyE23LbAeFHRnXgcNdc";}_sf2_meta|a:3:{s:1:"u";i:1562668662;s:1:"c";i:1562668662;s:1:"l";s:1:"0";}

routes.yaml

_sf2_attributes|a:2:{s:19:"_csrf/https-contact";s:43:"Oq-QpN21bI_BUDcVbv0ocyrYsTzQo3aJr80QAk2AR7w";s:19:"_csrf/https-booking";s:43:"z_L4TG7Wg0jydwl5VabfJMx0NBhQgeasuAiqxksLvD8";}_sf2_meta|a:3:{s:1:"u";i:1562668584;s:1:"c";i:1562668584;s:1:"l";s:1:"0";}

LoginFormAuthenticator

access_control:
    - { path: ^/odelices_admin, roles: ROLE_USER}

Contrôleur de sécurité

Current configuration for extension with alias "security"
=========================================================

security:
encoders:
    App\Entity\User:
        algorithm: bcrypt
        hash_algorithm: sha512
        key_length: 40
        ignore_case: false
        encode_as_base64: true
        iterations: 5000
        cost: null
        memory_cost: null
        time_cost: null
        threads: null
providers:
    app_user_provider:
        entity:
            class: App\Entity\User
            property: email
            manager_name: null
firewalls:
    dev:
        pattern: ^/(_(profiler|wdt)|css|images|js)/
        security: false
        methods: {  }
        user_checker: security.user_checker
        stateless: false
        logout_on_user_change: true
    main:
        anonymous:
            secret: null
        guard:
            authenticators:
                - App\Security\LoginFormAuthenticator
            entry_point: null
        logout:
            path: app_logout
            csrf_parameter: _csrf_token
            csrf_token_id: logout
            target: /
            invalidate_session: true
            delete_cookies: {  }
            handlers: {  }
        methods: {  }
        security: true
        user_checker: security.user_checker
        stateless: false
        logout_on_user_change: true
access_control:
    -
        path: ^/backoffice
        roles:
            - ROLE_EDITOR
        requires_channel: null
        host: null
        port: null
        ips: {  }
        methods: {  }
        allow_if: null
access_decision_manager:
    strategy: affirmative
    allow_if_all_abstain: false
    allow_if_equal_granted_denied: true
access_denied_url: null
session_fixation_strategy: migrate
hide_user_not_found: true
always_authenticate_before_granting: false
erase_credentials: true
role_hierarchy: {  }

ÉDITER:

php bin/console debug:config security sortie de php bin/console debug:config security

// use...

class SecurityController extends AbstractController
{
    /**
     * @Route("/login", name="app_login")
     */
    public function login(AuthenticationUtils $authenticationUtils): Response
    {
        // get the login error if there is one
        $error = $authenticationUtils->getLastAuthenticationError();
        // last username entered by the user
        $lastUsername = $authenticationUtils->getLastUsername();

        return $this->render(
          'security/login.html.twig',
          [
            'last_username' => $lastUsername,
            'error' => $error,
          ]
        );
    }

    /**
     * @Route("/logout", name="app_logout")
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    public function logout()
    {
        return $this->redirectToRoute('home');
    }
}
//... skipped forgottenPassword and resetPassword methods

MODIFIER 2

Comme @Arno l'a commenté, j'ai édité framework.yaml pour enregistrer les sessions dans le répertoire var/ et je peux vérifier que cette étape fonctionne sans problèmes d'autorisations, chaque fois que je frappe le formulaire de connexion, un fichier sess_ est écrit.

Cela vaut la peine de dire que si je commente:

// use...

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
    use TargetPathTrait;

    private $entityManager;
    private $urlGenerator;
    private $csrfTokenManager;
    private $passwordEncoder;

    public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->entityManager = $entityManager;
        $this->urlGenerator = $urlGenerator;
        $this->csrfTokenManager = $csrfTokenManager;
        $this->passwordEncoder = $passwordEncoder;
    }

    public function supports(Request $request)
    {
        return 'app_login' === $request->attributes->get('_route')
            && $request->isMethod('POST');
    }

    public function getCredentials(Request $request)
    {
        $credentials = [
            'email' => $request->request->get('email'),
            'password' => $request->request->get('password'),
            'csrf_token' => $request->request->get('_csrf_token'),
        ];
        $request->getSession()->set(
            Security::LAST_USERNAME,
            $credentials['email']
        );

        return $credentials;
    }

    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $token = new CsrfToken('authenticate', $credentials['csrf_token']);
        if (!$this->csrfTokenManager->isTokenValid($token)) {
            throw new InvalidCsrfTokenException();
        }

        $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);

        if (!$user) {
            // fail authentication with a custom error
            throw new CustomUserMessageAuthenticationException('Email could not be found.');
        }

        return $user;
    }

    public function checkCredentials($credentials, UserInterface $user)
    {
        return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
    }

    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
            return new RedirectResponse($targetPath);
        }
        return new RedirectResponse($this->urlGenerator->generate('admin'));
    }

    protected function getLoginUrl()
    {
        return $this->urlGenerator->generate('app_login');
    }
}

Je peux accéder au backoffice.

EDIT 3: comportement de session

Alors maintenant, les sessions sont enregistrées dans var / sessions / prod.

  1. Je nettoie le répertoire: sudo rm -r var/sessions/prod/sess_*
  2. J'ouvre Chrome et l'url, il définit un cookie PHPSSID avec la même valeur qu'un premier fichier sess_xyz:

    admin:
      path: /backoffice
      controller: EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController
    
  3. Je vais à la page de connexion. Nouvelle valeur PHPSSID associée à un nouveau fichier sess_xyz:

    security:
        encoders:
            App\Entity\User:
                algorithm: bcrypt
        providers:
            app_user_provider:
                entity:
                    class: App\Entity\User
                    property: email
        firewalls:
            dev:
                pattern: ^/(_(profiler|wdt)|css|images|js)/
                security: false
            main:
                anonymous: true
                guard:
                    authenticators:
                        - App\Security\LoginFormAuthenticator
                logout:
                    path: app_logout
        access_control:
            - { path: ^/backoffice, roles: ROLE_EDITOR} # requires_channel: https
    
  4. Je me connecte avec des valeurs correctes. Cela crée 3 nouveaux fichiers ssid_xyz.

    ### var/log/prod.log (same following lines, but from production server) 
    [2019-07-05 10:28:46] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
    [2019-07-05 10:28:46] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
    [2019-07-05 10:28:46] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
    [2019-07-05 10:28:46] security.INFO: Populated the TokenStorage with an anonymous Token. [] []
    [2019-07-05 10:28:46] security.DEBUG: Access denied, the user is not fully authenticated; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException(code: 403): Access Denied. at /var/www/clients/client0/web4/web/vendor/symfony/security-http/Firewall/AccessListener.php:72)"} []
    [2019-07-05 10:28:46] security.DEBUG: Calling Authentication entry point. [] []
    [2019-07-05 10:28:46] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\Controller\\SecurityController::login"},"request_uri":"https://example.com/login","method":"GET"} []
    

EDIT 4: entité utilisateur

### var/log/prod.log (following lines, localhost) 
[2019-07-05 10:19:29] security.DEBUG: Read existing security token from the session. {"key":"_security_main","token_class":"Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken"} []
[2019-07-05 10:19:29] security.DEBUG: User was reloaded from a user provider. {"provider":"Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider","username":"raoux.thierry@free.fr"} []
[2019-07-05 10:19:29] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
[2019-07-05 10:19:29] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:19:29] security.DEBUG: Guard authenticator does not support the request. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:19:29] cache.INFO: Lock acquired, now computing item "easyadmin.processed_config" {"key":"easyadmin.processed_config"} []

Empiler

Debian Stretch, Nginx + Varnish : Nginx gère 443 requêtes, les transmet à Varnish en tant que proxy de cache, qui fournit des objets mis en cache ou transmet des requêtes au backend nginx sur le port 8083. Cela fonctionne comme un charme pour une autre application avec une logique de connexion similaire (la seule différence est le buggy redirigé vers easyadmin au lieu d'un administrateur personnalisé), donc je ne pense pas que cela soit lié à la pile.

vhost

### var/log/prod.log output with info level
[2019-07-05 10:28:46] request.INFO: Matched route "app_login". {"route":"app_login","route_parameters":{"_route":"app_login","_controller":"App\\Controller\\SecurityController::login"},"request_uri":"https://example.com/login","method":"POST"} []
[2019-07-05 10:28:46] security.DEBUG: Checking for guard authentication credentials. {"firewall_key":"main","authenticators":1} []
[2019-07-05 10:28:46] security.DEBUG: Checking support on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:28:46] security.DEBUG: Calling getCredentials() on guard authenticator. {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:28:46] security.DEBUG: Passing guard token information to the GuardAuthenticationProvider {"firewall_key":"main","authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:28:46] php.INFO: User Deprecated: The "Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder" class is deprecated since Symfony 4.3, use "Symfony\Component\Security\Core\Encoder\NativePasswordEncoder" instead. {"exception":"[object] (ErrorException(code: 0): User Deprecated: The \"Symfony\\Component\\Security\\Core\\Encoder\\BCryptPasswordEncoder\" class is deprecated since Symfony 4.3, use \"Symfony\\Component\\Security\\Core\\Encoder\\NativePasswordEncoder\" instead. at /var/www/clients/client0/web4/web/vendor/symfony/security-core/Encoder/BCryptPasswordEncoder.php:14)"} []

[2019-07-05 10:28:46] security.INFO: Guard authentication successful! {"token":"[object] (Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken: PostAuthenticationGuardToken(user=\"myemail@gmail.com\", authenticated=true, roles=\"ROLE_EDITOR, ROLE_USER\"))","authenticator":"App\\Security\\LoginFormAuthenticator"} []

[2019-07-05 10:28:46] security.DEBUG: Guard authenticator set success response. {"response":"[object] (Symfony\\Component\\HttpFoundation\\RedirectResponse: HTTP/1.0 302 Found\r\nCache-Control: no-cache, private\r\nDate:          Fri, 05 Jul 2019 10:28:46 GMT\r\nLocation:      /backoffice\r\n\r\n<!DOCTYPE html>\n<html>\n    <head>\n        <meta charset=\"UTF-8\" />\n        <meta http-equiv=\"refresh\" content=\"0;url=/backoffice\" />\n\n        <title>Redirecting to /backoffice</title>\n    </head>\n    <body>\n        Redirecting to <a href=\"/backoffice\">/backoffice</a>.\n    </body>\n</html>)","authenticator":"App\\Security\\LoginFormAuthenticator"} []

[2019-07-05 10:28:46] security.DEBUG: Remember me skipped: it is not configured for the firewall. {"authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:28:46] security.DEBUG: The "App\Security\LoginFormAuthenticator" authenticator set the response. Any later authenticator will not be called {"authenticator":"App\\Security\\LoginFormAuthenticator"} []
[2019-07-05 10:28:46] security.DEBUG: Stored the security token in the session. {"key":"_security_main"} []

[2019-07-05 10:28:46] request.INFO: Matched route "easyadmin". {"route":"easyadmin","route_parameters":{"_controller":"Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction","path":"/backoffice/","permanent":true,"scheme":null,"httpPort":80,"httpsPort":443,"_route":"easyadmin"},"request_uri":"https://example.com/backoffice","method":"GET"} []

Cela pourrait-il être lié aux autorisations sur certains répertoires? HTTPS? EasyAdmin? Comment puis-je m'assurer que le jeton de sécurité a été stocké dans la session, même s'il est enregistré comme stocké? J'ai également essayé de changer access_control en rôle ROLE_USER afin que tout utilisateur authentifié puisse y accéder. En aucune façon.

Toute aide est vraiment appréciée.


10 commentaires

Pouvez-vous montrer utiliser votre configuration en utilisant le débogage php bin / console: config security


Avez-vous effacé le cache sur le serveur après le téléchargement?


Je viens de php bin/console debug:config security output. Oui, je recherche le problème depuis hier soir et j'ai vidé le cache environ 100 fois, vérifié les problèmes d'autorisations ...


Utilisez-vous les paramètres par défaut pour l'enregistrement des sessions (configurés sous framework.session)? Si tel est le cas, les sessions sont enregistrées en tant que fichiers et cela peut certainement être un problème d'autorisation. Par défaut, ils doivent être enregistrés dans un dossier "sessions" dans votre répertoire de cache, mais vous pouvez changer cela comme décrit ici: symfony.com/doc/3.4/session/sessions_directory.html . Vous pouvez peut-être essayer d'accorder des autorisations au répertoire 777. Il devrait créer un fichier par session au format "sess_ <id>" avec l'utilisateur sérialisé à l'intérieur.


@ArnoHilke Merci beaucoup pour cet indice. Eh bien, je suis passé à 4.3 doc puis j'ai changé ma configuration pour enregistrer explicitement les sessions dans var dir, et jusqu'à présent, la session y est écrite, donc malheureusement ce n'est pas le point de blocage: ((


Qu'en est-il du fait qu'une nouvelle valeur est donnée au ssid chaque fois que je frappe une URL (page de connexion ou non)? Est-ce un comportement normal ???


@Kojo L'ID de session doit changer à chaque fois que votre niveau d'authentification change, c'est-à-dire lors de la connexion et de la déconnexion, mais pas sur les requêtes normales. Une raison pourrait être que votre utilisateur change ou est détecté comme modifié, par exemple à cause d'une (dé) sérialisation défectueuse. Vous pouvez également vérifier 1) si une session appropriée est créée lorsque vous vous connectez (vérifiez le contenu du fichier pour un utilisateur avec une adresse e-mail et un rôle appropriés) et 2) si le cookie est correctement défini et envoyé avec cet ID de session sur votre client .


@ArnoHilke merci encore! J'ai édité le message avec tout le comportement. S'il vous plaît jeter un oeil, oui les cookies sont définis concernant les fichiers ssid, mais chaque fois que la redirection est effectuée, retour au début ... Je dois assister à des réunions d'équipe cet après-midi, je reviendrai après ... Je ne sais vraiment pas Que penser :-((


pouvons-nous voir votre entité utilisateur s'il vous plaît?


Veuillez également vérifier qu'il n'y a pas d'option stateless: true sous votre pare-feu. Cela bloquera le démarrage de session pour l'utilisateur, et tous resteront anonymes après une authentification réussie.


4 Réponses :


5
votes

Cela m'est arrivé il y a un an, authentification réussie, redirection et connexion anonyme. Donne-moi envie de me fracasser la tête contre les murs. Le problème que j'avais à l'époque était que j'ai créé l'utilisateur selon un ancien cours de KnpUniversity et qu'il n'a pas implémenté EquatableInterface et la méthode isEqualTo. J'espère que cela fonctionnera pour vous.

Assurez-vous que votre entité utilisateur implémente EquatableInterface

namespace App\Entity;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;

    class User implements UserInterface, EquatableInterface
{
    private $username;
    private $password;
    private $salt;
    private $roles;

    public function __construct($username, $password, $salt, array $roles)
    {
        $this->username = $username;
        $this->password = $password;
        $this->salt = $salt;
        $this->roles = $roles;
    }

    // your setters, getters and whatever ...

    public function isEqualTo(UserInterface $user)
    {
        if (!$user instanceof WebserviceUser) {
            return false;
        }

        if ($this->password !== $user->getPassword()) {
            return false;
        }

        if ($this->salt !== $user->getSalt()) {
            return false;
        }

        if ($this->username !== $user->getUsername()) {
            return false;
        }

        return true;
    }
}


4 commentaires

Merci Yoann. Cela ne change pas le comportement. : - ((je vais publier l'entité utilisateur


Vous vous demandez peut-être: j'ai effectué cette vérification lors de ma tentative d'implémentation d'EquatableInterface if ($this->email !== $user->getUsername()) car getUsername renvoie l'email tel que défini dans le fournisseur security.yaml ... cela fonctionne dans localhost, pas en prod


c'est étrange et peut-être lié à votre problème


Pour autant que je sache, par défaut, l'utilisateur est actualisé en comparant le dernier sérialisé avec le courant, en utilisant getSalt() , getPassword() et getUsername() de l'entité utilisateur. getUsername n'est pas getName() , il doit renvoyer une valeur unique, et vous définissez laquelle dans la propriété d'entité security.yaml -> fournisseurs. tu n'es pas d'accord? Votre implémentation fonctionne bien avec ma configuration, mais il n'y avait aucune raison d'ajouter cette vérification dans ma configuration si basique. Quoi qu'il en soit, grâce à vous, j'ai appris une nouvelle façon de personnaliser cette étape!



3
votes

Cela finit par dire que la configuration de Varnish était défectueuse.

J'ai complètement oublié que je devais spécifier n'importe quel modèle de backoffice tel que /admin/ , /backoffice/ et plus pour ne pas être mis en cache par le proxy , mais au lieu de cela directement transmis au backend .

sub vcl_recv {
    # ...
    if (req.url ~ "^/status\.php$" ||
        req.url ~ "^/update\.php$" ||
        req.url ~ "^/install\.php" ||
        req.url ~ "^/admin$" ||
        req.url ~ "^/admin/.*$" ||
        req.url ~ "^/flag/.*$" ||
        req.url ~ "^.*/ajax/.*$" ||
        req.url ~ "^.*/ahah/.*$" ||
        req.url ~ "^/info/.*$" ||
        req.url ~ "^/system/files/.*$" ||
        req.url ~ "^/user" ||
        req.url ~ "^/users/.*$" ||
        req.url ~ "^/user/.*$" ) {

       return (pass);
    }
    # ...

Cela était déjà configuré pour l'autre application symfony que je mentionne dans la question, et pour plusieurs sites Web Drupal. Au moins, cela m'a obligé à creuser profondément dans le processus d'authentification des utilisateurs Symfony 4 , et comment le déboguer! Peut-être que cet article de débogage étape par étape aidera d'autres lecteurs?!


0 commentaires

5
votes

Voici donc mes commentaires d'une manière plus structurée, afin que cela puisse aider quelqu'un d'autre ayant des problèmes d'authentification dans Symfony.

Assurez-vous que les sessions sont enregistrées

Par défaut, chaque session est enregistrée sous forme de fichier avec le nom sess_<id> dans <project_dir>/var/cache/<env>/sessions ou comme défini par save_path dans votre php.ini si framework.session.handler est défini sur null . Configurez votre répertoire de session de manière explicite et assurez-vous qu'un fichier de session est créé lorsque vous vous connectez. Sinon, vérifiez les autorisations pour ce dossier.

_sf2_attributes|a:3:{s:18:"_csrf/authenticate";s:43:"n2oap401u4P4O7m_IhPODZ6Bz7EHl-DDsHxBEl-fhxc";s:23:"_security.last_username";s:10:"foo@bar.de";s:14:"_security_main";s:545:"C:67:"Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken":464:{a:2:{i:0;s:4:"main";i:1;a:5:{i:0;O:15:"App\Entity\User":4:{s:19:"App\Entity\Userid";i:1;s:22:"App\Entity\Useremail";s:10:"foo@bar.de";s:22:"App\Entity\Userroles";a:0:{}s:25:"App\Entity\Userpassword";s:60:"$2y$13$qwbtasafa58lPonX6B5a9eV4lziF7EZWP8NFLAe3blpCJVhQgPVOS";}i:1;b:1;i:2;a:1:{i:0;O:41:"Symfony\Component\Security\Core\Role\Role":1:{s:47:"Symfony\Component\Security\Core\Role\Rolerole";s:9:"ROLE_USER";}}i:3;a:0:{}i:4;a:1:{i:0;s:9:"ROLE_USER";}}}}";}_sf2_meta|a:3:{s:1:"u";i:1563015142;s:1:"c";i:1563015142;s:1:"l";s:1:"0";}

Cf. https://symfony.com/doc/current/session.html#configuration

Assurez-vous que les sessions sont correctes et utilisées

Lorsque vous vous connectez, une session avec un nouvel ID doit être créée. Vérifiez le contenu du fichier. Il doit contenir votre utilisateur sérialisé sous le nom du pare-feu (par exemple, main), y compris votre identifiant (par exemple, email) et votre (vos) rôle (s) utilisateur (par exemple ROLE_USER). Un problème ici peut être causé par une authentification, une configuration de sécurité ou une sérialisation incorrectes.

# app/config/config.yml (Symfony 3)
# config/packages/framework.yaml (Symfony 4)
framework:
    session:
        handler_id: 'session.handler.native_file'
        save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'

Vérifiez qu'un cookie avec le même identifiant est défini dans votre navigateur lors de la connexion. Le nom du cookie est défini par session.name dans votre php.ini , par défaut c'est PHPSESSID . Il doit être envoyé avec chaque demande que vous faites (par exemple, Cookie: PHPSESSID=lpcf79ff8jdv2iigsgvepnr9bb ). Si la session correcte existe, mais que vous avez un cookie différent dans votre navigateur, vous avez peut-être été immédiatement déconnecté après une redirection réussie.

Assurez-vous que l'utilisateur est correctement actualisé

L'ID de session ne doit changer que lorsque votre utilisateur change (par exemple lors de la connexion et de la déconnexion). S'il change après des requêtes normales (par exemple, vous êtes immédiatement déconnecté) ou si votre session semble être ignorée, le problème peut être que Symfony considère que votre utilisateur a changé. Cela peut être causé par une (dé) sérialisation ou une comparaison défectueuse.

Par défaut, Symfony utilise les résultats sérialisés de getPassword() , getUsername() et getSalt() de la session pour les comparer avec l'utilisateur fourni par le fournisseur de l'utilisateur (par exemple la base de données). Si l'une de ces valeurs change, vous êtes déconnecté (cf. https://symfony.com/doc/current/security/user_provider.html#understanding-how-users-are-refreshed-from-the-session ).

Ainsi, vous devez vous assurer que l'utilisateur fourni par exemple par votre base de données est correct et correspond à l'utilisateur désérialisé de la session. Assurez-vous que les champs pertinents sont correctement sérialisés. Si vous implémentez l'interface Serializable , assurez-vous que votre méthode serialize() correspond à votre unserialize() . Si vous implémentez EquatableInterface , assurez-vous que votre méthode isEqualTo() fonctionne correctement. Cependant, ces deux interfaces sont facultatives, vous pouvez donc envisager de les supprimer à des fins de débogage.


1 commentaires

Le "isEqualTo ()" et les méthodes de sérialisation ont fonctionné pour moi, merci!



0
votes

J'ai eu le même symptôme que celui décrit par l'OP mais dans une application Symfony 5.1 et en utilisant ma propre entité utilisateur existante. Il s'avère que le problème est que je n'ai pas implémenté \ Serializable sur mon entité utilisateur et les 2 méthodes requises:

public function serialize()
{
    return serialize(array(
        $this->id,
        $this->username,
        $this->password,
        // see section on salt below
        // $this->salt,
    ));
}

/** @see \Serializable::unserialize() */
public function unserialize($serialized)
{
    list (
        $this->id,
        $this->username,
        $this->password,
        // see section on salt below
        // $this->salt
    ) = unserialize($serialized, array('allowed_classes' => false));
}

Une fois que cela a été corrigé, l'application a pu rester connectée à la fois dans les environnements de développement et de production, et avec et sans l'option "Se souvenir de moi". La logique derrière cela est expliquée sur le site officiel .


0 commentaires