<i id='GWj6w'><tr id='GWj6w'><dt id='GWj6w'><q id='GWj6w'><span id='GWj6w'><b id='GWj6w'><form id='GWj6w'><ins id='GWj6w'></ins><ul id='GWj6w'></ul><sub id='GWj6w'></sub></form><legend id='GWj6w'></legend><bdo id='GWj6w'><pre id='GWj6w'><center id='GWj6w'></center></pre></bdo></b><th id='GWj6w'></th></span></q></dt></tr></i><div id='GWj6w'><tfoot id='GWj6w'></tfoot><dl id='GWj6w'><fieldset id='GWj6w'></fieldset></dl></div>
    <bdo id='GWj6w'></bdo><ul id='GWj6w'></ul>
  • <tfoot id='GWj6w'></tfoot>

    <legend id='GWj6w'><style id='GWj6w'><dir id='GWj6w'><q id='GWj6w'></q></dir></style></legend>

      <small id='GWj6w'></small><noframes id='GWj6w'>

      1. Symfony 5.1:使用实体用户提供者的 LDAP 身份验证

        时间:2024-08-23
        <legend id='2vOjg'><style id='2vOjg'><dir id='2vOjg'><q id='2vOjg'></q></dir></style></legend>

        • <bdo id='2vOjg'></bdo><ul id='2vOjg'></ul>
          • <i id='2vOjg'><tr id='2vOjg'><dt id='2vOjg'><q id='2vOjg'><span id='2vOjg'><b id='2vOjg'><form id='2vOjg'><ins id='2vOjg'></ins><ul id='2vOjg'></ul><sub id='2vOjg'></sub></form><legend id='2vOjg'></legend><bdo id='2vOjg'><pre id='2vOjg'><center id='2vOjg'></center></pre></bdo></b><th id='2vOjg'></th></span></q></dt></tr></i><div id='2vOjg'><tfoot id='2vOjg'></tfoot><dl id='2vOjg'><fieldset id='2vOjg'></fieldset></dl></div>

                <tfoot id='2vOjg'></tfoot>
                  <tbody id='2vOjg'></tbody>

                <small id='2vOjg'></small><noframes id='2vOjg'>

                  本文介绍了Symfony 5.1:使用实体用户提供者的 LDAP 身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

                  问题描述

                  我正在使用 Symfony 5.1 并尝试实现 LDAP 身份验证,而用户属性(用户名、角色等)存储在 MySQL 数据库中.因此,我为 Doctrine 添加了一个用户实体,并配置了文档对应的 services.yml 和 security.yml.

                  I'm using Symfony 5.1 and trying to implement a LDAP Authentication, while the User Properties (Username, Roles, etc.) are stored in a MySQL DB. Thus I added a User Entity for Doctrine and configurated the services.yml and security.yml corresponding to the Documentation.

                  我还使用 Maker Bundle 生成了一个 LoginFormAuthenticator,它似乎使用了 Guard Authenticator 模块.

                  I also used the Maker Bundle to generate a LoginFormAuthenticator which seems to use the Guard Authenticator Module.

                  当我尝试登录时,它看起来好像没有做任何与 LDAP 相关的事情.我还用 tcpdump 监听了 TCP 包,没有看到任何到 LDAP 服务器的流量.

                  When I'm trying to login it simply looks like it is not doing anything LDAP related. I also listened the TCP packages with tcpdump and didn't see any traffic to the LDAP server.

                  这是我的代码:

                  services.yml:

                  services:
                      _defaults:
                          autowire: true      # Automatically injects dependencies in your services.
                          autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
                  
                      App:
                          resource: '../src/*'
                          exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
                  
                      AppController:
                          resource: '../src/Controller'
                          tags: ['controller.service_arguments']
                  
                      SymfonyComponentLdapLdap:
                          arguments: ['@SymfonyComponentLdapAdapterExtLdapAdapter']
                      SymfonyComponentLdapAdapterExtLdapAdapter:
                          arguments:
                              -   host: <ldap-IP>
                                  port: 389
                                  options:
                                      protocol_version: 3
                                      referrals: false
                  

                  security.yml:

                  security:
                      encoders:
                          AppEntityUser:
                              algorithm: auto
                  
                          app_user_provider:
                              entity:
                                  class: AppEntityUser
                                  property: email
                  
                          my_ldap:
                              ldap:
                                  service: SymfonyComponentLdapLdap
                                  base_dn: "<base_dn>"
                                  search_dn: "<search_dn>"
                                  search_password: "<password>"
                                  default_roles: ROLE_USER
                                  uid_key: sAMAccountName
                  
                      firewalls:
                          dev:
                              pattern: ^/(_(profiler|wdt)|css|images|js)/
                              security: false
                          main:
                              anonymous: true
                              lazy: true
                              provider: my_ldap
                  
                              form_login_ldap:
                                  login_path: login
                                  check_path: login
                                  service: SymfonyComponentLdapLdap
                                  dn_string: 'uid={username},OU=Test,DC=domain,DC=domain'
                  
                              guard:
                                  authenticators:
                                      - AppSecurityLoginFormAuthenticator
                              logout:
                                  path: app_logout
                                  # where to redirect after logout
                                  target: index
                  
                      access_control:
                          - { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
                          - { path: ^/admin, roles: ROLE_ADMIN }
                          - { path: ^/profile, roles: ROLE_USER }
                  

                  LoginFormAuthenticator,我猜问题出在 checkCredentials 函数中.我发现 LdapBindAuthenticationProvider 类的目的似乎正是针对 LDAP 进行此类用户凭据检查,但我完全不确定该怎么做:

                  The LoginFormAuthenticator, I guess the issue lies here within the checkCredentials function. I found the LdapBindAuthenticationProvider class which's purpose seems to be exactly such user credential checking agains LDAP, but I'm totally unsure how I have to do it:

                  <?php
                  
                  namespace AppSecurity;
                  
                  use PsrLogLoggerInterface;
                  use AppEntityUser;
                  use DoctrineORMEntityManagerInterface;
                  use SymfonyComponentHttpFoundationRedirectResponse;
                  use SymfonyComponentHttpFoundationRequest;
                  use SymfonyComponentRoutingGeneratorUrlGeneratorInterface;
                  use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
                  use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface;
                  use SymfonyComponentSecurityCoreExceptionCustomUserMessageAuthenticationException;
                  use SymfonyComponentSecurityCoreExceptionInvalidCsrfTokenException;
                  use SymfonyComponentSecurityCoreSecurity;
                  use SymfonyComponentSecurityCoreUserUserInterface;
                  use SymfonyComponentSecurityCoreUserUserProviderInterface;
                  use SymfonyComponentSecurityCsrfCsrfToken;
                  use SymfonyComponentSecurityCsrfCsrfTokenManagerInterface;
                  use SymfonyComponentSecurityGuardAuthenticatorAbstractFormLoginAuthenticator;
                  use SymfonyComponentSecurityGuardPasswordAuthenticatedInterface;
                  use SymfonyComponentSecurityHttpUtilTargetPathTrait;
                  use SymfonyComponentLdapLdap;
                  use SymfonyComponentSecurityCoreAuthenticationProviderLdapBindAuthenticationProvider;
                  
                  class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
                  {
                      use TargetPathTrait;
                  
                      public const LOGIN_ROUTE = 'app_login';
                  
                      private $logger;
                      private $entityManager;
                      private $urlGenerator;
                      private $csrfTokenManager;
                      private $passwordEncoder;
                      private $ldap;
                  
                      public function __construct(LoggerInterface $logger, EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder, Ldap $ldap, LdapBindAuthenticationProvider $form_login_ldap)
                      {
                          $this->logger = $logger;
                          $this->entityManager = $entityManager;
                          $this->urlGenerator = $urlGenerator;
                          $this->csrfTokenManager = $csrfTokenManager;
                          $this->passwordEncoder = $passwordEncoder;
                          $this->ldap = $ldap;
                      }
                  
                      public function supports(Request $request): ?bool
                      {
                          return self::LOGIN_ROUTE === $request->attributes->get('_route')
                              && $request->isMethod('POST');
                      }
                  
                      public function getCredentials(Request $request)
                      {
                          $credentials = [
                              'username' => $request->request->get('username'),
                              'password' => $request->request->get('password'),
                              'csrf_token' => $request->request->get('_csrf_token'),
                          ];
                          $request->getSession()->set(
                              Security::LAST_USERNAME,
                              $credentials['username']
                          );
                  
                          return $credentials;
                      }
                  
                      public function getUser($credentials, UserProviderInterface $userProvider)
                      {
                          $token = new CsrfToken('authenticate', $credentials['csrf_token']);
                          if (!$this->csrfTokenManager->isTokenValid($token)) {
                              throw new InvalidCsrfTokenException();
                          }
                  
                          $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['username']]);
                  
                          if (!$user) {
                              // user not found in db, but may exist in ldap:
                              $user = $userProvider->loadUserByUsername($credentials['username']);
                              if (!$user) {
                                  // user simply doesn't exist
                                  throw new CustomUserMessageAuthenticationException('Email could not be found.');
                              } else {
                                  // user never logged in before, create user in DB and proceed...
                                  // TODO
                              }
                          }
                  
                          return $user;
                      }
                  
                      public function checkCredentials($credentials, UserInterface $user)
                      {
                          // TODO: how to use the LdapBindAuthenticationProvider here to check the users credentials agains LDAP?
                          return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
                      }
                  
                      /**
                       * Used to upgrade (rehash) the user's password automatically over time.
                       */
                      public function getPassword($credentials): ?string
                      {
                          return $credentials['password'];
                      }
                  
                      public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
                      {
                          if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
                              return new RedirectResponse($targetPath);
                          }
                  
                          // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
                          throw new Exception('TODO: provide a valid redirect inside '.__FILE__);
                      }
                  
                      protected function getLoginUrl()
                      {
                          return $this->urlGenerator->generate(self::LOGIN_ROUTE);
                      }
                  }
                  

                  很遗憾,我没有找到任何示例代码.

                  Unfortunately I didn't find any example code for this.

                  感谢 T. van den Berg 的回答,我终于设法让身份验证部分正常工作.我从 security.yml 中删除了 LoginFormAuthenticator Guard,并稍微调整了 form_login_ldap.

                  Thanks to the answer of T. van den Berg I finally managed to get the authentication part working. I removed the LoginFormAuthenticator Guard from the security.yml and tweaked the form_login_ldap a little bit.

                  security:
                      encoders:
                          AppEntityUser:
                              algorithm: auto
                  
                      providers:
                          app_user_provider:
                              entity:
                                  class: AppEntityUser
                                  property: email
                  
                          my_ldap:
                              ldap:
                                  service: SymfonyComponentLdapLdap
                                  base_dn: '<baseDN>'
                                  search_dn: '<bindDN>'
                                  search_password: '<bindDN password>'
                                  default_roles: ['ROLE_USER']
                  
                      firewalls:
                          dev:
                              pattern: ^/(_(profiler|wdt)|css|images|js)/
                              security: false
                  
                          main:
                              anonymous: true
                              lazy: true
                              provider: my_ldap
                  
                              form_login_ldap:
                                  login_path: app_login
                                  check_path: app_login
                                  service: SymfonyComponentLdapLdap
                                  dn_string: '<baseDN>'
                                  query_string: '(sAMAccountName={username})'
                                  search_dn: '<bindDN>'
                                  search_password: '<bindDN password>'
                  
                              logout:
                                  path: app_logout
                                  target: index
                  
                      access_control:
                          - { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
                          - { path: ^/admin, roles: ROLE_ADMIN }
                          - { path: ^/profile, roles: ROLE_USER }
                  

                  它现在使用 LDAPUserProvider 使用 LDAP 服务用户(绑定 DN)通过其登录名(sAMAccountName)获取用户 LDAP 对象,然后在第二个请求中使用此 LDAP 对象的专有名称(DN)来使用提供的密码对 LDAP 服务器进行另一次身份验证.到目前为止还不错.

                  It is now using the LDAPUserProvider to use the LDAP service user (bind DN) to fetch the user LDAP object by its login name (sAMAccountName) and then in a second request use the distinguished name (DN) of this LDAP object to make another authentication against the LDAP server with the provided password. That's fine so far.

                  唯一缺少的是数据库存储的用户实体.我的想法如下:

                  The only thing missing is the database stored User entity. My Idea was as follows:

                  • 登录表单已提交
                  • 使用提供的用户名在数据库中搜索用户实体
                  • 如果未找到用户实体,请使用 LDAPUserProvider 向 LDAP 询问用户名
                  • 如果用户存在于 LDAP 中,则在数据库中创建一个用户实体
                  • 使用提供的密码针对 LDAP 对用户进行身份验证

                  密码未保存在数据库中,但其他应用程序特定信息在 LDAP 中不可用(例如上次活动).

                  The password is not saved in the database, but other application specific information not available in LDAP (e.g. last activity).

                  有人知道如何实现吗?

                  推荐答案

                  如果您想将 LDAP 用户保存到登录后的本地数据库.

                  You can use this bundle ldaptools/ldaptools-bundle (or Maks3w/FR3DLdapBundle) if you want to save your LDAP user to a local database after they login.

                  有关更多信息,请参阅:https://github.com/ldaptools/ldaptools-bundle/blob/master/Resources/doc/Save-LDAP-Users-to-the-Database-After-Login.md

                  for more information see: https://github.com/ldaptools/ldaptools-bundle/blob/master/Resources/doc/Save-LDAP-Users-to-the-Database-After-Login.md

                  这就是我的工作方式(没有外部捆绑包):

                  1. Security.yaml

                  security:
                      encoders:
                          AppEntityUser:
                              algorithm: auto
                  
                      # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
                      providers:
                          # used to reload user from session & other features (e.g. switch_user)
                          app_user_provider:
                              entity:
                                  class: AppEntityUser
                                  property: username
                  
                          ldap_server:
                              ldap:
                                  service: SymfonyComponentLdapLdap
                                  base_dn: "dc=example,dc=com"
                                  search_dn: "cn=read-only-admin,dc=example,dc=com"
                                  search_password: "password"
                                  default_roles: ROLE_USER
                                  uid_key: uid
                  
                          chain_provider:
                              chain:
                                  providers: [ 'app_user_provider', 'ldap_server' ]
                  
                      firewalls:
                          dev:
                              pattern: ^/(_(profiler|wdt)|css|images|js)/
                              security: false
                          main:
                              anonymous: lazy
                              provider: chain_provider
                  
                  
                              form_login:
                                  login_path: app_login
                                  check_path: app_login
                  
                              form_login_ldap:
                                  login_path: app_login
                                  check_path: app_login
                                  service: SymfonyComponentLdapLdap
                                  dn_string: 'uid={username},dc=example,dc=com'
                  
                              logout:
                                  path: app_logout
                  
                  

                  1. 事件监听器

                  <?php
                  
                  
                  namespace AppEventListener;
                  
                  
                  use AppEntityUser;
                  use DoctrineORMEntityManagerInterface;
                  use SymfonyComponentSecurityCoreEncoderUserPasswordEncoderInterface;
                  use SymfonyComponentSecurityHttpEventInteractiveLoginEvent;
                  
                  class LoginEventListener
                  {
                      /**
                       * @var EntityManagerInterface
                       */
                      protected $em;
                  
                      /**
                       * @var UserPasswordEncoderInterface
                       */
                      private $encoder;
                  
                      /**
                       * LoginEventListener constructor.
                       * @param EntityManagerInterface $em
                       * @param UserPasswordEncoderInterface $encoder
                       */
                      public function __construct(EntityManagerInterface $em, UserPasswordEncoderInterface $encoder)
                      {
                          $this->em = $em;
                          $this->encoder = $encoder;
                      }
                  
                      /**
                       * @param InteractiveLoginEvent $event
                       */
                      public function onLoginSuccess(InteractiveLoginEvent $event)
                      {
                          $request = $event->getRequest();
                          $token = $event->getAuthenticationToken();
                          $loggedUser = $token->getUser();
                  
                  //     If the logged user is not an instance of User (not ldapUser), then it hasn't been saved to the database. So save it..
                          if(!($loggedUser instanceof User)) {
                              $user = new User();
                              $user->setUsername($request->request->get('_username'));
                              $user->setPassword($this->encoder->encodePassword($user, $request->request->get('_password')));
                              $user->setRoles($loggedUser->getRoles());
                              $this->em->persist($user);
                              $this->em->flush();
                          }
                  
                      }
                  
                  

                  1. services.yaml

                  # ldap service
                      SymfonyComponentLdapLdap:
                          arguments: [ '@SymfonyComponentLdapAdapterExtLdapAdapter' ]
                      SymfonyComponentLdapAdapterExtLdapAdapter:
                          arguments:
                              - host: ldap.forumsys.com
                                port: 389
                                options:
                                    protocol_version: 3
                                    referrals: false
                  
                      app_bundle.event.login_listener:
                          class: AppEventListenerLoginEventListener
                          arguments: [ '@doctrine.orm.entity_manager', '@security.user_password_encoder.generic' ]
                          tags:
                              - { name: kernel.event_listener, event: security.interactive_login, method: onLoginSuccess }
                  
                  

                  这篇关于Symfony 5.1:使用实体用户提供者的 LDAP 身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

                  上一篇:如何使用 LDAP 和 PHP 从 Active Directory 安全组中检索用户信息 下一篇:用于区分名称的 ldap 过滤器

                  相关文章

                • <legend id='9l9u2'><style id='9l9u2'><dir id='9l9u2'><q id='9l9u2'></q></dir></style></legend>

                    <small id='9l9u2'></small><noframes id='9l9u2'>

                    • <bdo id='9l9u2'></bdo><ul id='9l9u2'></ul>
                    1. <i id='9l9u2'><tr id='9l9u2'><dt id='9l9u2'><q id='9l9u2'><span id='9l9u2'><b id='9l9u2'><form id='9l9u2'><ins id='9l9u2'></ins><ul id='9l9u2'></ul><sub id='9l9u2'></sub></form><legend id='9l9u2'></legend><bdo id='9l9u2'><pre id='9l9u2'><center id='9l9u2'></center></pre></bdo></b><th id='9l9u2'></th></span></q></dt></tr></i><div id='9l9u2'><tfoot id='9l9u2'></tfoot><dl id='9l9u2'><fieldset id='9l9u2'></fieldset></dl></div>
                      <tfoot id='9l9u2'></tfoot>