使用 ldaptools+jwt+fosuser 在 symfony api 中进行身份验证

authentication in symfony api with ldaptools+jwt+fosuser

在我的 api 中,我尝试使用 LdapToolsBundle, FOSUserBundle and LexikJWTAuthenticationBundle. Doing things step by step and following the integration docs for fosuser and ldaptools 验证用户身份,后来我设法完成以下 jwt 文档:

  1. FosUserBundle + LdapToolsBundle 成功
  2. Api 集成 + FosUserBundle + LdapToolsBundle 成功
  3. Api 上的 Jwt + FosUserBundle + LdapToolsBundle 失败。

问题是我只能登录我的数据库而不能登录 ldap。

在我的数据库中,我有一个用 fos 命令和任何密码创建的用户记录(确保身份验证是在 ldap 而不是 fosuser 上)。到目前为止,一切都很好。但是一旦引入 JWT,身份验证就由 fosuser 而不是 ldap 身份验证卫士进行。当我使用 fos 命令更改密码时,我可以毫无问题地获取令牌。

这是我的配置:

security:
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt
        LdapTools\Bundle\LdapToolsBundle\Security\User\LdapUser: plaintext

    role_hierarchy:
        ROLE_ADMIN:       ROLE_USER
        ROLE_SUPER_ADMIN: ROLE_ADMIN

    providers:
        fos_userbundle:
            id: fos_user.user_provider.username_email

        ldap:
            id: ldap_tools.security.user.ldap_user_provider

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

        api_login:
            pattern:  ^/login
            stateless: true
            provider: fos_userbundle
            anonymous: true
            form_login:
                check_path:               /login
                require_previous_session: false
                username_parameter:       username
                password_parameter:       password
                success_handler:          lexik_jwt_authentication.handler.authentication_success
                failure_handler:          lexik_jwt_authentication.handler.authentication_failure
                require_previous_session: false
            guard:
                authenticators:
                    - ldap_tools.security.ldap_guard_authenticator
            logout: true

        api:
            pattern:   ^/
            stateless: true
            lexik_jwt: ~

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

我的问题是如何使用 ldap 验证器?我将展示所需的工作流程

  1. 已收到授权请求(用户名和密码)
  2. fosuser 提供者发现用户或未找到用户(错误凭证-->结束)
  3. ldap authenticator guard 根据域服务器或错误凭据对用户进行身份验证 -->end
  4. 用户登录成功,收到token

但我在数据库和域服务器中的注册用户的凭据仍然错误(用户凭据有效)

提前致谢!

深入研究 ldap_tools.security.ldap_guard_authenticator 验证器(namespace LdapTools\Bundle\LdapToolsBundle\Security;)我发现了这个

 public function getUser($credentials, UserProviderInterface $userProvider)
{
    $domain = $this->ldap->getDomainContext();

    try {
        $credDomain = isset($credentials['ldap_domain']) ? $credentials['ldap_domain'] : '';
        $this->switchDomainIfNeeded($credDomain);
        $this->setLdapCredentialsIfNeeded($credentials['username'], $credentials['password'], $userProvider);
        $user = $userProvider->loadUserByUsername($credentials['username']);
        $this->userChecker->checkPreAuth($user);

        return $user;
    } catch (UsernameNotFoundException $e) {
        $this->hideOrThrow($e, $this->options['hide_user_not_found_exceptions']);
    } catch (BadCredentialsException $e) {
        $this->hideOrThrow($e, $this->options['hide_user_not_found_exceptions']);
    } catch (LdapConnectionException $e) {
        $this->hideOrThrow($e, $this->options['hide_user_not_found_exceptions']);
    } catch (\Exception $e) {
        $this->hideOrThrow($e, $this->options['hide_user_not_found_exceptions']);
    } finally {
        $this->switchDomainBackIfNeeded($domain);
    }
}

此方法在给定用户提供者和一些凭据的情况下加载用户。

因此,getUser 依赖于用户提供者来加载任何用户,但由于您使用 fos_userbundle 作为 api_login 防火墙的用户提供者,实际上您是在针对您的本地用户进行身份验证数据库。尝试在您的配置中使用 ldap userprovider。

当然,这样做您将针对 ldap 服务器进行身份验证,而不是您上面描述的流程。为此,请考虑自己处理身份验证,以便您可以根据需要处理流程。 你也可以这样做 customauthenticator 并且在您的 public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) 中,您可以使用 "@ldap_tools.ldap_manager"->authenticate($user, $password, &$errorMessage = false, &$errorNumber = false).

服务检查您的 ldap 服务器的凭据

这是我的答案:检查 ldap 服务器后强制授权即可。

public function onAuthenticationFailure(AuthenticationFailureEvent $event)//when auth fails on DB(Allways!!!)
    {
        $userToken = $event->getAuthenticationToken();
        $username = $userToken->getUsername();
        $password = $this->requestStack->getCurrentRequest()->get('password');

        if ($this->ldapManager->authenticate($username, $password)) {//good credentials
            $token = new UsernamePasswordToken($userToken, 'yes', "public", $userToken->getRoles());
            $this->container->get('security.token_storage')->setToken($token);//set a token

            $event = new InteractiveLoginEvent($this->requestStack->getCurrentRequest(), $token); //dispatch the auth event
            $event->stopPropagation();
            $this->container->get('event_dispatcher')->dispatch(SecurityEvents::INTERACTIVE_LOGIN,$event);

        }
        //symfony takes care of the response
        }

到目前为止,这是我找到的最佳答案