首页 文章

Symfony 3:如何在Authenticator checkCredentials中检查LDAP

提问于
浏览
1

我需要使用自定义逻辑对LDAP服务器进行用户身份验证 .

我正在Symfony 3.3中实现自定义身份验证系统,并构建了一个名为 LoginFormAuthenticator 的自定义身份验证器,其扩展名为 AbstractFormLoginAuthenticator ,如下所示:http://symfony.com/doc/current/security/guard_authentication.html

我需要针对数据库中的User实体检查用户名,然后根据用户的类型,针对存储在数据库中的bcrypt密码进行身份验证,或者针对外部LDAP服务器进行身份验证 .

在checkCredentials方法内部,我可以成功验证存储在数据库中的密码:

class LoginFormAuthenticator extends AbstractFormLoginAuthenticator {
     ...

            public function checkCredentials($credentials, UserInterface $user)
            {
               ...

               // check password if the user is database user
               if ($user->getApp() == 'DB') {
                 if ($this->passwordEncoder->isPasswordValid($user, $password)) {
                      return true;
                 }
               }

               // check LDAP server if LDAP user
               if ($this->getApp() == 'LDAP') {
                if ($this->unknownLdapService->check($user, $password)
                {
                      return true;
                }
               ...

我不清楚使用本机symfony功能检查LDAP服务器的用户名和密码的正确方法 .

如果我更改我的配置以使用form_login_ldap(而不是我的新身份验证器),它实际上确实成功地对LDAP进行了身份验证,尽管它使得调用对我来说是混淆的 .

我应该用什么服务或类来查询LDAP来代替上面的 unknownLdapService

2 回答

  • 0

    您可以使用自己的LDAP服务:您只需要调用ldap_bind . (它可以让你做更多的ldap检查或模拟它)

    您可以使用Symfony提供程序:vendor / symfony / symfony / src / Symfony / Component / Security / Core / Authentication / Provider / LdapBindAuthenticationProvider.php

    namespace Symfony\Component\Security\Core\Authentication\Provider;
    
    class LdapBindAuthenticationProvider extends UserAuthenticationProvider
    {
        private $userProvider;
        private $ldap;
        private $dnString;
    
        public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, LdapClientInterface $ldap, $dnString = '{username}', $hideUserNotFoundExceptions = true)
        {
            parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
    
            $this->userProvider = $userProvider;
            $this->ldap = $ldap;
            $this->dnString = $dnString;
        }
    
        /**
         * {@inheritdoc}
         */
        protected function retrieveUser($username, UsernamePasswordToken $token)
        {
            if ('NONE_PROVIDED' === $username) {
                throw new UsernameNotFoundException('Username can not be null');
            }
    
            return $this->userProvider->loadUserByUsername($username);
        }
    
        /**
         * {@inheritdoc}
         */
        protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
        {
            $username = $token->getUsername();
            $password = $token->getCredentials();
    
            if ('' === $password) {
                throw new BadCredentialsException('The presented password must not be empty.');
            }
    
            try {
                $username = $this->ldap->escape($username, '', LDAP_ESCAPE_DN);
                $dn = str_replace('{username}', $username, $this->dnString);
    
                $this->ldap->bind($dn, $password);
            } catch (ConnectionException $e) {
                throw new BadCredentialsException('The presented password is invalid.');
            }
        }
    }
    
  • 0

    我最终在这里使用的解决方案是我首先将现有的Symfony ldap服务注入到我的方法的构造函数中 . ldap服务在_921480中配置,与Symfony docs为 form_login_ldap 提供程序配置它的方式相同 .

    /**
     * LoginFormAuthenticator constructor.
     * @param FormFactoryInterface $formFactory
     * @param EntityManager $em
     * @param RouterInterface $router
     * @param SecureUserPasswordEncoder $passwordEncoder
     * @param Ldap $ldap
     */
    public function __construct(..., Ldap $ldap, ... )
    {
        ...
        $this->ldap = $ldap;
    }
    

    然后在checkCredentials方法中,我调用了ldap bind方法:

    public function checkCredentials($credentials, $userInterface $user)
        ...
        $password = $credentials['_password']; 
        $login_format = 'DOMAIN\%s';  // this is the expected format in my case
        $login_username = sprintf($login_format, $user);
        ...
        try {
            // try to bind with the username and provided password
            $this->ldap->bind($login_username, $password);
        } catch (\Symfony\Component\Ldap\Exception\ConnectionException $e) {
            //return false;
            throw new CustomUserMessageAuthenticationException('The submitted LDAP password is invalid.');
        };
            return true;
        };
    

    这是有效的,如果ldap auth失败,它会抛出相应的异常 .

相关问题