2015-09-09 48 views
1

我有一個基於Symfony2的項目,我自己編碼驗證(基於文檔和食譜)。該項目有兩種類型的用戶:一些用LDAP認證,其他用戶用數據庫憑證。 問題是,如果有更多的用戶登錄他們的會話頻繁丟失(5分鐘或更少),他們需要再次登錄(目前我在開發環境)。 有什麼我可以做錯了(雖然我不這麼認爲),或者這是一個Symfony2錯誤或PHP設置造成這種情況? 任何幫助深表謝意。Symfony2經常失敗登錄

監聽器:

class LdapListener implements ListenerInterface 
{ 
    protected $tokenStorage; 
    protected $authenticationManager; 

    public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager) 
    { 
     $this->tokenStorage = $tokenStorage; 
     $this->authenticationManager = $authenticationManager; 
    } 

    public function handle(GetResponseEvent $event) 
    { 

     $request = $event->getRequest(); 

     $username = $request->request->get('_username'); 
     $password = $request->request->get('_password'); 

     if ($username == null OR $password == null) 
     { 
      return; 
     } 

     $token = new LdapUserToken(); 
     $token->setUser($username); 
     $token->setPassword($password); 

     try 
     { 
      $authenticationToken = $this->authenticationManager->authenticate($token); 
      $this->tokenStorage->setToken($authenticationToken); 
     } 
     catch (AuthenticationException $exception) 
     { 
      throw new AuthenticationException('Denied'); 
     } 

     return; 
    } 
} 

身份驗證提供:

class LdapProvider implements AuthenticationProviderInterface { 

    private $userProvider; 
    private $em; 

    const ENC_KEY = 'SECRETKEY'; 

    public function __construct(UserProviderInterface $userProvider, $doctrine) { 
     $this->em   = $doctrine->getManager(); 
     $this->userProvider = $userProvider; 
    } 

    public function authenticate(TokenInterface $token) { 
     $user = $this->userProvider->loadUserByUsername($token->getUsername()); 

     if ($user && $user->getDomainUser() && $this->validateCredentials(
       $token->getUsername(), 
       $token->getPassword() 
      ) 
     ) { 
      if ($token->getPassword() != '') { 
       $savePwd = $this->em->getRepository('LoginProfileBundle:LdapUser')->find($user->getId()); 
       $savePwd->setMailPassword($this->encrypt($token->getPassword())); 
       $savePwd->setLastLogin(new \DateTime()); 
       $this->em->persist($savePwd); 
       $this->em->flush(); 
      } 

      $authenticatedToken = new LdapUserToken($user->getRoles()); 
      $authenticatedToken->setUser($user); 

      return $authenticatedToken; 
     } 
     elseif ($user && trim($token->getPassword()) == trim($this->decrypt($user->getRealPassword()))) { 

      if ($token->getPassword() != '') { 
       $savePwd = $this->em->getRepository('LoginProfileBundle:LdapUser')->find($user->getId()); 
       $savePwd->setLastLogin(new \DateTime()); 
       $this->em->persist($savePwd); 
       $this->em->flush(); 
      } 

      $authenticatedToken = new LdapUserToken($user->getRoles()); 
      $authenticatedToken->setUser($user); 

      return $authenticatedToken; 
     } 

     throw new AuthenticationException('LdapProvider: Invalid login details'); 
    } 

    public function encrypt($pure_string) { 
     $iv_size   = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB); 
     $iv    = mcrypt_create_iv($iv_size, MCRYPT_RAND); 
     $encrypted_string = mcrypt_encrypt(
      MCRYPT_BLOWFISH, 
      self::ENC_KEY, 
      utf8_encode($pure_string), 
      MCRYPT_MODE_ECB, 
      $iv 
     ); 

     return base64_encode($encrypted_string); 
    } 


    public function decrypt($encrypted_string) { 
     if (trim($encrypted_string) == '' || $encrypted_string == null) { 
      return 'n'; 
     } 

     $iv_size = mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB); 
     $iv  = mcrypt_create_iv($iv_size, MCRYPT_RAND); 

     $encrypted_string = base64_decode($encrypted_string); 
     $decrypted_string = mcrypt_decrypt(MCRYPT_BLOWFISH, self::ENC_KEY, $encrypted_string, MCRYPT_MODE_ECB, $iv); 

     return $decrypted_string; 
    } 

    public function supports(TokenInterface $token) { 
     return $token instanceof LdapUserToken; 
    } 

    public function validateCredentials($username, $password) { 
     $this->ldapLink = ldap_connect('SOMESERVERNAME'); 

     if (!$this->ldapLink) { 
      return false; 
     } 

     ldap_set_option($this->ldapLink, LDAP_OPT_PROTOCOL_VERSION, 3); 

     if ([email protected]_bind($this->ldapLink, $username, $password)) { 
      return null; 
     } 
     else { 
      return true; 
     } 
    } 
} 

令牌:

class LdapUserToken extends AbstractToken { 

    private $password; 

    public function __construct(array $roles = array()) { 
     parent::__construct($roles); 
     $this->setAuthenticated(count($roles) > 0); 
    } 

    public function getCredentials() { 
     return ''; 
    } 

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

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

Security.yml

security: 
encoders: 
    Login\ProfileBundle\Entity\LdapUser: plaintext 

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

providers: 
    in_domain: 
     entity: 
      class: Login\ProfileBundle\Entity\LdapUser 
      property: username 

firewalls: 
    default: 
     anonymous: ~ 
     http_basic: ~ 
     ldap: true 
     provider: in_domain 
     form_login: 
      login_path: /login 
      check_path: /login_check 
     logout: 
      path: /logout 
      target: /login 
     switch_user: true 
access_control: 
    - { path: ^/_wdt, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
    - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } 
    - { path: ^/, roles: ROLE_USER } 

配置:

services: 
    twig.extension.intl: 
    class: Twig_Extensions_Extension_Intl 
    tags: 
     - { name: twig.extension } 
ldap.security.authentication.provider: 
    class: Login\ProfileBundle\Security\Authentication\Provider\LdapProvider 
    arguments: ["", "@doctrine"] 

ldap.security.authentication.listener: 
    class: Login\ProfileBundle\Security\Firewall\LdapListener 
    arguments: ["@security.token_storage", "@security.authentication.manager"] 

ldap_user_provider: 
    class: Login\ProfileBundle\Security\User\LdapUserProvider 
+0

聽起來像我這樣的會話配置問題。也許[垃圾收集器](http:// php。net/manual/en/session.configuration.php)踢得更快?我想到的另一件事是,Symfony應用程序和LDAP服務器之間的連接可能會下降,因此您的用戶詳細信息不能從數據庫中加載,這會導致Symfony將您視爲來賓用戶? – tftd

+0

垃圾回收的速度與登錄人數有關嗎?如果我是唯一登錄會話的人幾乎有效。如果另外2-3-4個用戶上線,他們需要在幾分鐘後重新登錄.....這也發生在非LDAP用戶身上。 –

+0

在這種情況下,我不知道可能會出現什麼問題。我一直在使用[這個包](https://packagist.org/packages/imag/ldap-bundle)進行LDAP身份驗證,並使用postgres數據庫進行身份驗證。到目前爲止我還沒有遇到任何問題。這可能是你的實現? – tftd

回答

0

不幸的是我沒有創建一個工作示例給你看它是如何做的時間。然而,我可以告訴你的是,你的實施方向是朝着正確的方向發展,但並不完全。


在你的情況,你需要有一個自定義的UserProvider負責從源加載你的用戶。源可以是任何你想要的 - 數據庫(通過教條),文件,LDAP等。你的UserProvider類必須實現UserProviderInterfacedocumentation)。然後,您應該在您的services.yml中註冊該課程。事情是這樣的:

services: 
    my.app.ldap.user.provider: 
     class: App\YourBundle\Provider\LdapUserProvider 

在這個類中,你應該把你現在在你的LdapProvider(方法encryptdecryptvalidateCredentials以及與加載從源頭上用戶的所有其他的東西)。 一旦與UserProvider類來完成,你需要把它添加到您的security.yml

security: 
    providers: 
     user_provider: 
      id: my.app.ldap.user.provider 

然後,你需要創建一個AuthenticationProvider。這個類必須實現AuthenticationProviderInterface並且負責根據一些可能的定製邏輯對用戶進行認證。如果用戶成功通過身份驗證,則會生成Token。有關如何實現這一點的更多信息,請閱讀具有完整示例的official documentation


該任務將需要一段時間才能正確實施。我會建議你使用imag/ldap-bundle這已經實施和工作。您可以配置兩個LDAP &數據庫身份驗證。如果你仍然決定實施你自己的認證服務提供商,你可以看到它是如何完成的,並且有一些路線圖。

+0

感謝您的幫助,但我有一個AuthenticationProvider。身份驗證正常,一切正常,直到其他人登錄。然後我被隨機踢出(失去認證)。 –