【问题标题】:Symfony ldap authentification with crediantials带有凭据的 Symfony ldap 身份验证
【发布时间】:2018-06-06 10:16:33
【问题描述】:

Ldap 用户有动态 dn,必须按照用户名搜索用户以获取他的 dn 并与之绑定

我只有一个用户源(sql),但我希望在 ldap 或数据库中完成密码检查

使用此配置,用户已通过身份验证,但未加载到数据库中并且没有正确的角色

如果我在 form_login_ldap 中删除提供者:my_chain_provider

我有这个错误:“如果不先绑定连接,就无法执行查询。”

我找不到合适的配置

security.yaml

security:
providers:
    my_chain_provider:
        chain:
            providers: [fos_userbundle, my_ldap]
    my_ldap:
        ldap:
            service: Symfony\Component\Ldap\Ldap
            base_dn: DC=domain,DC=be
            search_dn: "CN=admin,OU=apps,DC=domain,DC=be"
            search_password: mdp
            default_roles: ROLE_USER
    fos_userbundle:
        id: fos_user.user_provider.username_email

encoders:
    Symfony\Component\Security\Core\User\User: bcrypt
    AcDomain\Travaux\Entity\Security\User: bcrypt # ou sha512

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

    secured_area:
        pattern:    ^/
        provider: fos_userbundle
        form_login:
            provider: my_chain_provider
            use_referer: true
        form_login_ldap:
            provider: my_chain_provider
            service: Symfony\Component\Ldap\Ldap
            dn_string: 'DC=domain,DC=be'
            query_string: 'sAMAccountName=*{username}*'
        logout:       true
        anonymous:    true

access_control:
    - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }

service.yaml

Symfony\Component\Ldap\Ldap:
    arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
    arguments:
        -   host: ldap.domain.be
            port: 636
            encryption: ssl
            options:
                protocol_version: 3
                referrals: false

谢谢

【问题讨论】:

    标签: php symfony authentication ldap symfony4


    【解决方案1】:

    我找到了正确的配置

    service.yaml

    Symfony\Component\Ldap\Ldap:
        arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
    Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
        arguments:
            -   host: '%env(ACLDAP_URL)%'
                port: 636
                encryption: ssl
                options:
                    protocol_version: 3
                    referrals: false
    

    security.yaml

    security:
    providers:
        sql_provider:
            id: AcMarche\Travaux\Security\UserProvider
        ldap_provider:
            ldap:
                service: Symfony\Component\Ldap\Ldap
                base_dn: '%env(ACLDAP_DN)%'
                search_dn: '%env(ACLDAP_USER)%'
                search_password: '%env(ACLDAP_PASSWORD)%'
                default_roles: ROLE_USER
                uid_key: sAMAccountName
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
    
        secured_area:
            pattern:    ^/
            provider: sql_provider
            form_login:
                use_referer: true
                remember_me: true
            form_login_ldap:
                provider: ldap_provider
                use_referer: true
                remember_me: true
                service: Symfony\Component\Ldap\Ldap
                dn_string: '%env(ACLDAP_DN)%'
                query_string: '(&(|(sAMAccountName=*{username}*))(objectClass=person))'
    

    但是有了这个配置我还有一个问题:

    我的用户在 datable 中使用密码进行身份验证,用户加载了我的实体:AcDomain\Travaux\Entity\Security\User

    但是如果他的 ldap 用户的日志加载了实体 symfony 核心: Symfony\Component\Security\Core\User\User

    所以我用guard FormLoginAuthenticator创建了自己的身份验证

    form_login:
                use_referer: true
                remember_me: true
            guard:
                authenticators:
                    - AcDomain\Travaux\Security\FormLoginAuthenticator
    

    FormLoginAuthenticator.php

        class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
    {
        use TargetPathTrait;
    
        private $userRepository;
        private $router;
        private $encoder;
        private $ldap;
    /**
     * @var CsrfTokenManagerInterface
     */
    private $csrfTokenManager;
    
    public function __construct(
        UserRepository $userRepository,
        UserPasswordEncoderInterface $encoder,
        RouterInterface $router,
        StaffLdap $ldap,
        CsrfTokenManagerInterface $csrfTokenManager
    ) {
        $this->router = $router;
        $this->userRepository = $userRepository;
        $this->encoder = $encoder;
        $this->ldap = $ldap;
        $this->csrfTokenManager = $csrfTokenManager;
    }
    
    /**
     * Return the URL to the login page.
     *
     * @return string
     */
    protected function getLoginUrl()
    {
        return $this->router->generate('travaux_login');
    }
    
    /**
     * Does the authenticator support the given Request?
     *
     * If this returns false, the authenticator will be skipped.
     *
     * @param Request $request
     *
     * @return bool
     */
    public function supports(Request $request)
    {
        if ($request->getPathInfo() == '/login_check' && $request->isMethod('POST')) {
            return true;
        }
    
        return false;
    }
    
    /**
     * Get the authentication credentials from the request and return them
     * as any type (e.g. an associate array).
     *
     * Whatever value you return here will be passed to getUser() and checkCredentials()
     *
     * For example, for a form login, you might:
     *
     *      return array(
     *          'username' => $request->request->get('_username'),
     *          'password' => $request->request->get('_password'),
     *      );
     *
     * Or for an API token that's on a header, you might use:
     *
     *      return array('api_key' => $request->headers->get('X-API-TOKEN'));
     *
     * @param Request $request
     *
     * @return mixed Any non-null value
     *
     * @throws \UnexpectedValueException If null is returned
     */
    public function getCredentials(Request $request)
    {
        return [
            'username' => $request->request->get('_username'),
            'password' => $request->request->get('_password'),
            'token' => $request->request->get('_csrf_token'),
        ];
    }
    
    /**
     * Return a UserInterface object based on the credentials.
     *
     * The *credentials* are the return value from getCredentials()
     *
     * You may throw an AuthenticationException if you wish. If you return
     * null, then a UsernameNotFoundException is thrown for you.
     *
     * @param mixed $credentials
     * @param UserProviderInterface $userProvider
     *
     * @throws AuthenticationException
     *
     * @return UserInterface|null
     */
    public function getUser($credentials, UserProviderInterface $userProvider)
    {
        $username = $credentials['username'];
        $user = null;
    
        try {
            return $this->userRepository->loadUserByUsername($username);
        } catch (NonUniqueResultException $e) {
        }
    
        return null;
    }
    
    /**
     * Returns true if the credentials are valid.
     *
     * If any value other than true is returned, authentication will
     * fail. You may also throw an AuthenticationException if you wish
     * to cause authentication to fail.
     *
     * The *credentials* are the return value from getCredentials()
     *
     * @param mixed $credentials
     * @param UserInterface $user
     *
     * @return bool
     *
     * @throws AuthenticationException
     */
    public function checkCredentials($credentials, UserInterface $user)
    {
        $token = $credentials['token'];
    
        if (false === $this->csrfTokenManager->isTokenValid(new CsrfToken('authenticate', $token))) {
            throw new InvalidCsrfTokenException('Invalid CSRF token.');
        }
    
        $entry = $this->ldap->getEntry($user->getUsername());
    
        if ($entry instanceof Entry) {
            $dn = $entry->getDn();
    
            try {
                $this->ldap->bind($dn, $credentials['password']);
    
                return true;
            } catch (\Exception $exception) {
                //throw new BadCredentialsException($exception->getMessage());
            }
        }
    
        //try check password in db
        return $this->encoder->isPasswordValid($user, $credentials['password']);
    }
    
    /**
     * Called when authentication executed and was successful!
     *
     * This should return the Response sent back to the user, like a
     * RedirectResponse to the last page they visited.
     *
     * If you return null, the current request will continue, and the user
     * will be authenticated. This makes sense, for example, with an API.
     *
     * @param Request $request
     * @param TokenInterface $token
     * @param string $providerKey The provider (i.e. firewall) key
     *
     * @return Response|null
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
    {
        $targetPath = $this->getTargetPath($request->getSession(), $providerKey);
    
        return new RedirectResponse($targetPath);
    
       // return new RedirectResponse($this->router->generate('intervention'));
    }
    

    }

    StaffLdap.php

    class StaffLdap
    {
        private $ldap;
        private $dn;
    
    public function __construct(string $host, $dn, string $user, string $password)
    {
        $this->ldap = Ldap::create(
            'ext_ldap',
            array(
                'host' => $host,
                'encryption' => 'ssl',
            )
        );
    
        $this->dn = $dn;
        $this->ldap->bind($user, $password);
    }
    
    /**
     * @param $uid
     * @return \Symfony\Component\Ldap\Entry|null
     *
     */
    public function getEntry($uid)
    {
        $filter = "(&(|(sAMAccountName=*$uid*))(objectClass=person))";
        $query = $this->ldap->query($this->dn, $filter, ['maxItems' => 1]);
        $results = $query->execute();
    
        if ($results->count() > 0) {
            return $results[0];
        }
    
        return null;
    }
    
    /**
     * @param $user
     * @param $password
     * @throws LdapException
     */
    public function bind($user, $password)
    {
        try {
            $this->ldap->bind($user, $password);
        } catch (\Exception $exception) {
            throw new BadCredentialsException($exception->getMessage());
        }
    }
    
    /**
     * @return \Symfony\Component\Ldap\Adapter\EntryManagerInterface
     */
    public function getEntryManager()
    {
        return $this->ldap->getEntryManager();
    }
    

    }

    service.yaml

    AcDomain\Travaux\Security\StaffLdap:
        $host: '%env(ACLDAP_URL)%'
        $dn: '%env(ACLDAP_DN)%'
        $user: '%env(ACLDAP_USER)%'
        $password: '%env(ACLDAP_PASSWORD)%'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多