使用 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 文档:
- FosUserBundle + LdapToolsBundle 成功
- Api 集成 + FosUserBundle + LdapToolsBundle 成功
- 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 验证器?我将展示所需的工作流程
- 已收到授权请求(用户名和密码)
- fosuser 提供者发现用户或未找到用户(错误凭证-->结束)
- ldap authenticator guard 根据域服务器或错误凭据对用户进行身份验证 -->end
- 用户登录成功,收到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
}
到目前为止,这是我找到的最佳答案
在我的 api 中,我尝试使用 LdapToolsBundle, FOSUserBundle and LexikJWTAuthenticationBundle. Doing things step by step and following the integration docs for fosuser and ldaptools 验证用户身份,后来我设法完成以下 jwt 文档:
- FosUserBundle + LdapToolsBundle 成功
- Api 集成 + FosUserBundle + LdapToolsBundle 成功
- 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 验证器?我将展示所需的工作流程
- 已收到授权请求(用户名和密码)
- fosuser 提供者发现用户或未找到用户(错误凭证-->结束)
- ldap authenticator guard 根据域服务器或错误凭据对用户进行身份验证 -->end
- 用户登录成功,收到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 服务器后强制授权即可。
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
}
到目前为止,这是我找到的最佳答案