2016-10-05 4 views
6

Ich habe versucht, einen Authentifikator für mein Login-Formular zu erstellen, aber ich bin immer aus einem unklaren Grund nicht angemeldet.Symfony & Guard: "Das Sicherheitstoken wurde aufgrund einer AccountStatusException entfernt"

[2016-10-05 18:54:53] security.INFO: Guard authentication successful! {"token":"[object] (Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken: PostAuthenticationGuardToken(user=\"[email protected]\", authenticated=true, roles=\"ROLE_USER\"))","authenticator":"AppBundle\\Security\\Authenticator\\FormLoginAuthenticator"} [] 
[2016-10-05 18:54:54] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExpiredException(code: 0): at /space/products/insurance/vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php:86)"} [] 
[2016-10-05 18:54:54] security.INFO: The security token was removed due to an AccountStatusException. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExpiredException(code: 0): at /space/products/insurance/vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php:86)"} [] 

Ich verstehe nicht, diese „AuthenticationExpiredException“, wie ich nichts staatenlos, noch irgendeine Ablauf in irgendeiner Weise nirgendwo in meiner app.

Spricht dieses Problem mit niemandem?


Edit 1

Nach ein paar Stunden, es sieht aus wie ich wegen der {{ is_granted('ROLE_USER') }} in Twig unlogged bin. Ich verstehe sowieso nicht warum.

bearbeiten 2

Wenn ich Dump() mein Sicherheitszeichen auf die Methode des onAuthenticationSuccess Authenticator, authenticated = true.

Aber, wenn ich mein Sicherheitstoken nach einer Umleitung oder beim Zugriff auf eine neue Seite() dump() 'authenticated' = false.

Warum zur Hölle meine Authentifizierung wird nicht gespeichert.


app/config/security.yml

security: 

    encoders: 
     AppBundle\Security\User\Member: 
      algorithm: bcrypt 
      cost: 12 

    providers: 
     members: 
      id: app.provider.member 

    role_hierarchy: 
     ROLE_ADMIN:  "ROLE_USER" 

    firewalls: 
     dev: 
      pattern: "^/(_(profiler|wdt|error)|css|images|js)/" 
      security: false 

     main: 
      pattern: "^/" 
      anonymous: ~ 
      logout: ~ 
      guard: 
       authenticators: 
        - app.authenticator.form_login 

    access_control: 
     - { path: "^/connect", role: "IS_AUTHENTICATED_ANONYMOUSLY" } 
     - { path: "^/register", role: "IS_AUTHENTICATED_ANONYMOUSLY" } 
     - { path: "^/admin", role: "ROLE_ADMIN" } 
     - { path: "^/user", role: "ROLE_USER" } 
     - { path: "^/logout", role: "ROLE_USER" } 

AppBundle/Controller/SecurityController.php

<?php 

namespace AppBundle\Controller; 

use AppBundle\Base\BaseController; 
use AppBundle\Form\Type\ConnectType; 
use AppBundle\Security\User\Member; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template; 
use Symfony\Component\HttpFoundation\Request; 

class SecurityController extends BaseController 
{ 
    /** 
    * @Route("/connect", name="security_connect") 
    * @Template() 
    */ 
    public function connectAction(Request $request) 
    { 
     $connectForm = $this 
      ->createForm(ConnectType::class) 
      ->handleRequest($request) 
     ; 

     return [ 
      'connect' => $connectForm->createView(), 
     ]; 
    } 
} 

AppBundle/Form/Typ/ConnectType.php

<?php 

namespace AppBundle\Form\Type; 

use Symfony\Component\Form\AbstractType; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\Form\Extension\Core\Type; 
use Symfony\Component\Validator\Constraints; 
use EWZ\Bundle\RecaptchaBundle\Form\Type\EWZRecaptchaType; 
use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrue as RecaptchaTrue; 

class ConnectType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('email', Type\EmailType::class, [ 
       'label' => 'Your email', 
       'required' => true, 
       'constraints' => [ 
        new Constraints\Length(['min' => 8]) 
       ], 
      ]) 
      ->add('password', Type\PasswordType::class, [ 
       'label'  => 'Your password', 
       'constraints' => new Constraints\Length(['min' => 8, 'max' => 4096]), /* CVE-2013-5750 */ 
      ]) 
      ->add('recaptcha', EWZRecaptchaType::class, [ 
       'label'  => 'Please tick the checkbox below', 
       'constraints' => [ 
        new RecaptchaTrue() 
       ], 
      ]) 
      ->add('submit', Type\SubmitType::class, [ 
       'label' => 'Connect', 
      ]) 
     ; 
    } 
} 

AppBundle/Sicherheit/Authenticator/FormLoginAuthenticator.php

<?php 

namespace AppBundle\Security\Authenticator; 

use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator; 
use Symfony\Component\DependencyInjection\ContainerInterface; 
use Symfony\Component\HttpFoundation\Request; 
use Symfony\Component\Security\Core\User\UserInterface; 
use Symfony\Component\Security\Core\User\UserProviderInterface; 
use Symfony\Component\Security\Core\Exception\BadCredentialsException; 
use AppBundle\Form\Type\ConnectType; 

class FormLoginAuthenticator extends AbstractFormLoginAuthenticator 
{ 
    private $container; // ¯\_(ツ)_/¯ 

    public function __construct(ContainerInterface $container) 
    { 
     $this->container = $container; 
    } 

    public function getCredentials(Request $request) 
    { 
     if ($request->getPathInfo() !== '/connect') { 
      return null; 
     } 

     $connectForm = $this 
      ->container 
      ->get('form.factory') 
      ->create(ConnectType::class) 
      ->handleRequest($request) 
     ; 

     if ($connectForm->isValid()) { 
      $data = $connectForm->getData(); 

      return [ 
       'username' => $data['email'], 
       'password' => $data['password'], 
      ]; 
     } 

     return null; 
    } 

    public function getUser($credentials, UserProviderInterface $userProvider) 
    { 
     return $userProvider->loadUserByUsername($credentials['username']); 
    } 

    public function checkCredentials($credentials, UserInterface $user) 
    { 
     $isValid = $this 
      ->container 
      ->get('security.password_encoder') 
      ->isPasswordValid($user, $credentials['password']) 
     ; 

     if (!$isValid) { 
      throw new BadCredentialsException(); 
     } 

     return true; 
    } 

    protected function getLoginUrl() 
    { 
     return $this 
      ->container 
      ->get('router') 
      ->generate('security_connect') 
     ; 
    } 

    protected function getDefaultSuccessRedirectUrl() 
    { 
     return $this 
      ->container 
      ->get('router') 
      ->generate('home') 
     ; 
    } 
} 

AppBundle/Sicherheit/Provider/MemberProvider.php

<?php 

namespace AppBundle\Security\Provider; 

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 AppBundle\Security\User\Member; 
use Api\Gateway\RequestResponse\RequestResponseHandlerInterface; 
use Api\Business\InsuranceWebsite\Action\GetInsuranceMember\GetInsuranceMemberRequest; 
use Api\Gateway\Exception\NoResultException; 

class MemberProvider implements UserProviderInterface 
{ 
    protected $gateway; 

    public function __construct(RequestResponseHandlerInterface $gateway) 
    { 
     $this->gateway = $gateway; 
    } 

    public function loadUserByUsername($username) 
    { 
     try { 
      $response = $this->gateway->handle(
       new GetInsuranceMemberRequest($username) 
      ); 
     } catch (NoResultException $ex) { 
      throw new UsernameNotFoundException(
       sprintf('Username "%s" does not exist.', $username) 
      ); 
     } 

     $member = new Member(); 
     $member->setId($response->getId()); 
     $member->setUsername($response->getEmail()); 
     $member->setPassword($response->getPassword()); 
     $member->setCompanyId($response->getCompanyId()); 
     $member->setFirstname($response->getFirstname()); 
     $member->setLastname($response->getLastname()); 
     $member->setIsManager($response->isManager()); 
     $member->setIsEnabled($response->isEnabled()); 

     return $member; 
    } 

    public function refreshUser(UserInterface $user) 
    { 
     if (!$user instanceof Member) { 
      throw new UnsupportedUserException(
       sprintf('Instances of "%s" are not supported.', get_class($user)) 
      ); 
     } 

     return $this->loadUserByUsername($user->getUsername()); 
    } 

    public function supportsClass($class) 
    { 
     return $class === Member::class; 
    } 
} 

AppBundle/Sicherheit/Benutzer/Mitglied. php

<?php 

namespace AppBundle\Security\User; 

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

class Member implements UserInterface 
{ 
    private $id; 
    private $username; 
    private $password; 
    private $companyId; 
    private $firstname; 
    private $lastname; 
    private $isManager; 
    private $isEnabled; 
    private $roles = ['ROLE_USER']; 

    public function getId() 
    { 
     return $this->id; 
    } 

    public function setId($id) 
    { 
     $this->id = $id; 

     return $this; 
    } 

    public function getUsername() 
    { 
     return $this->username; 
    } 

    public function setUsername($username) 
    { 
     $this->username = $username; 

     return $this; 
    } 

    public function getPassword() 
    { 
     return $this->password; 
    } 

    public function setPassword($password) 
    { 
     $this->password = $password; 
     return $this; 
    } 

    public function getCompanyId() 
    { 
     return $this->companyId; 
    } 

    public function setCompanyId($companyId) 
    { 
     $this->companyId = $companyId; 

     return $this; 
    } 

    public function getFirstname() 
    { 
     return $this->firstname; 
    } 

    public function setFirstname($firstname) 
    { 
     $this->firstname = $firstname; 

     return $this; 
    } 

    public function getLastname() 
    { 
     return $this->lastname; 
    } 

    public function setLastname($lastname) 
    { 
     $this->lastname = $lastname; 

     return $this; 
    } 

    public function isManager() 
    { 
     return $this->isManager; 
    } 

    public function setIsManager($isManager) 
    { 
     $this->isManager = $isManager; 

     return $this; 
    } 

    public function IsEnabled() 
    { 
     return $this->isEnabled; 
    } 

    public function setIsEnabled($isEnabled) 
    { 
     $this->isEnabled = $isEnabled; 

     return $this; 
    } 

    public function eraseCredentials() 
    { 
     $this->password = null; 
    } 

    public function hasRole($role) 
    { 
     return in_array($role, $this->roles); 
    } 

    public function getRoles() 
    { 
     return $this->roles; 
    } 

    public function addRole($role) 
    { 
     if (!$this->hasRole($role)) { 
      $this->roles[] = $role; 
     } 

     return $this; 
    } 

    public function removeRole($role) 
    { 
     $index = array_search($role, $this->roles); 
     if ($index !== false) { 
      unset($this->roles[$index]); 
      $this->roles = array_values($this->roles); 
     } 

     return $this; 
    } 

    public function getSalt() 
    { 
     return null; 
    } 
} 

src/AppBundle/Ressourcen/config/services.yml

imports: 

parameters: 
    app.provider.member.class: AppBundle\Security\Provider\MemberProvider 
    app.authenticator.form_login.class: AppBundle\Security\Authenticator\FormLoginAuthenticator 

services: 
    app.provider.member: 
     class: %app.provider.member.class% 
     arguments: ['@gateway'] 

    app.authenticator.form_login: 
     class: %app.authenticator.form_login.class% 
     arguments: ["@service_container"] 

Antwort

14

fand ich meinen Fehler, nach 8 Stunden harter Arbeit. Ich verspreche, ich werde eine Menge Bier nach diesem Kommentar trinken!

I lag mein Problem in der Symfony\Component\Security\Core\Authentication\Token\AbstractToken::hasUserChanged() Methode, die in der Sitzung, und die eine zurück vom refreshUser Ihres Providers gespeichert Benutzer vergleicht.

wurde Mein Benutzer Einheit betrachtet wegen dieser Zustand geändert:

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

In der Tat, bevor sie in der Sitzung gespeichert werden, die eraseCredentials() Methode basiert auf Ihrem Benutzer Entität namens so das Passwort entfernt wird. Aber das Passwort existiert in dem Benutzer, den der Provider zurückgibt.

, deshalb, in Dokumentationen, sie zeigen plainPassword und password Eigenschaften ... Sie halten password in der Sitzung, und eraseCredentials reinigt nur bis `Plain. Irgendwie knifflig.

Se haben wir 2 Lösungen:

  • mit eraseCredentials nicht durch ein Passwort zu berühren, kann nützlich sein, wenn Sie Ihr Mitglied unauthent wollen, wenn er sein Passwort ändert sich irgendwie.

  • Implementierung EquatableInterface in unserer Benutzereinheit, weil der folgende Test vor dem oben genannten aufgerufen wird.

    if ($this->user instanceof EquatableInterface) { 
        return !(bool) $this->user->isEqualTo($user); 
    } 
    

entschied ich mich EquatableInterface in meiner Benutzereinheit zu implementieren, und ich werde nie vergessen, es in Zukunft zu tun.

<?php 

namespace AppBundle\Security\User; 

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

class Member implements UserInterface, EquatableInterface 
{ 

    // (...) 

    public function isEqualTo(UserInterface $user) 
    { 
     return $user->getId() === $this->getId(); 
    } 
} 
Verwandte Themen