Symfony 3.4 覆盖内置 LdapUserProvider 中的单个方法

Symfony 3.4 Override a single method in the built-in LdapUserProvider

当用户通过 LDAP 登录时,我正在尝试根据他们的 LDAP dn 向用户授予角色。 为此,我想覆盖 Symfony\Component\Security\Core\User\LdapUserProvider 中的 loadUser 方法,但我真的不知道如何继续,如果我理解正确的话(我对使用 Symfony 还很陌生 :p),它是不是服务,而是服务的一部分? 那么,有没有办法轻松覆盖该方法,或者我是否需要重新定义整个 Ldap 服务?

我试过的是:

// app/config/services.yml
[...]
Symfony\Component\Ldap\Ldap:
    arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
Symfony\Component\Security\Core\User\LdapUserProvider:
    class: AppBundle\Services\LdapUserProvider
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
    arguments:
        - host: '%ldap_host%'
          port: '%ldap_port%'
// src/Services/LdapUserProvider.php
namespace AppBundle\Services;

use Symfony\Component\Ldap\Entry;

class LdapUserProvider extends \Symfony\Component\Security\Core\User\LdapUserProvider {
    protected function loadUser($username, Entry $entry)
    {
        $password = null;

        if (null !== $this->passwordAttribute) {
            $password = $this->getAttributeValue($entry, $this->passwordAttribute);
        }

        return new User($username, $password, array('ROLE_TEST'));
    }
}

但当然这不起作用,我没有得到 ROLE_TEST 角色。

提前致谢!

您可以使用本教程:

https://medium.com/@devstan/extended-ldap-with-symfony-3-30be6f1a36b1

你应该创建一个 LdapUser class 来实现 UserInterface

1.LDAP 用户提供商

<?php
namespace YourAppBundle\Security\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Ldap\Entry;
use Symfony\Component\Security\Core\User\LdapUserProvider as BaseLdapUserProvider;
class LdapUserProvider extends BaseLdapUserProvider
{
    /**
     * {@inheritdoc}
     */
    public function refreshUser(UserInterface $user)
    {
        if (!$user instanceof LdapUser) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }
        return new LdapUser($user->getUsername(), null, $user->getRoles(), $user->getLdapEntry());
    }
    /**
     * {@inheritdoc}
     */
    public function supportsClass($class)
    {
        return $class === 'YourAppBundle\Security\User\LdapUser';
    }
    /**
     * {@inheritdoc}
     */
    protected function loadUser($username, Entry $entry)
    {
        $user = parent::loadUser($username, $entry);
        return new LdapUser($username, $user->getPassword(), $user->getRoles(), $entry);
    }
}

2.LDAP 用户

    <?php
    namespace YourAppBundle\Security\User;
    use Symfony\Component\Security\Core\User\UserInterface;
    use Symfony\Component\Ldap\Entry;
    class LdapUser implements UserInterface
    {
        const LDAP_KEY_DISPLAY_NAME = 'displayName';
        const LDAP_KEY_MAIL = 'mail';
        protected $username;
        protected $password;
        protected $roles;
        protected $ldapEntry;
        protected $displayName;
        protected $eMail;
        public function __construct($username, $password, array $roles, Entry $ldapEntry)
        {
            if ('' === $username || null === $username) {
                throw new \InvalidArgumentException('The username cannot be empty.');
            }
            $this->username    = $username;
            $this->password    = $password;
            $this->roles       = $roles;
            $this->ldapEntry   = $ldapEntry;
            $this->displayName = $this->extractSingleValueByKeyFromEntry(
                $ldapEntry,
                self::LDAP_KEY_DISPLAY_NAME,
                $username
            );
            $this->eMail       = $this->extractSingleValueByKeyFromEntry($ldapEntry, self::LDAP_KEY_MAIL);
        }
        public function __toString()
        {
            return (string) $this->getUsername();
        }
        public function getDisplayName()
        {
            return $this->displayName;
        }
        /**
         * @return Entry
         */
        public function getLdapEntry()
        {
            return $this->ldapEntry;
        }
        /**
         * {@inheritdoc}
         */
        public function getRoles()
        {
            return $this->roles;
        }
        /**
         * {@inheritdoc}
         */
        public function getPassword()
        {
            return $this->password;
        }
        /**
         * {@inheritdoc}
         */
        public function getSalt()
        {
        }
        /**
         * {@inheritdoc}
         */
        public function getUsername()
        {
            return $this->username;
        }
        /**
         * {@inheritdoc}
         */
        public function eraseCredentials()
        {
        }
        /**
         * Extracts single value from entry's array value by key.
         *
         * @param Entry       $entry        Ldap entry
         * @param string      $key          Key
         * @param null|string $defaultValue Default value
         *
         * @return string|null
         */
        protected function extractSingleValueByKeyFromEntry(Entry $entry, $key, $defaultValue = null)
        {
            $value = $this->extractFromLdapEntry($entry, $key, $defaultValue);
            return is_array($value) && isset($value[0]) ? $value[0] : $defaultValue;
        }
        /**
         * Extracts value from entry by key.
         *
         * @param Entry  $entry        Ldap entry
         * @param string $key          Key
         * @param mixed  $defaultValue Default value
         *
         * @return array|mixed
         */
        protected function extractFromLdapEntry(Entry $entry, $key, $defaultValue = null)
        {
            if (!$entry->hasAttribute($key)) {
                return $defaultValue;
            }
            return $entry->getAttribute($key);
        }
    }

3.Services.yml

我们需要定义我们新创建的 ldap 用户提供程序服务

    services:

        ...
    app.ldap:
            class: Symfony\Component\Ldap\Ldap
            factory: ['Symfony\Component\Ldap\Ldap', 'create']
            arguments:
                - 'ext_ldap'
                - host: '%ldap_host%'
    app.ext_ldap_user_provider:
                class: YourAppBundle\Security\User\LdapUserProvider
                arguments:
                    - '@app.ldap'               # LDAP component instance
                    - '%ldap_base_dn%'          # Base dn
                    - '%ldap_search_dn%'        # Search dn
                    - '%ldap_search_password%'  # Search user password
                    - ['ROLE_SUPER_ADMIN']      # Roles
                    - '%ldap_uid_key%'          # LDAP uid key
                    - '%ldap_filter%'           # filter

4.Security.yml

这里只是我们提供者的一个使用示例 “/ui”将使用 LDAP 进行保护。

security:
     encoders:

     ...
     YourAppBundle\Security\User\LdapUser:
            algorithm: bcrypt
            cost: 12
     providers:

     ...
        ldap_users:
            id: app.ext_ldap_user_provider
...
     firewalls:
         frontend:
            anonymous: ~
            provider: ldap_users
            form_login_ldap:
                service: app.ldap
                dn_string: '%ldap_dn_string%'
                login_path: front-login
                check_path: front-login
                default_target_path: /ui
            logout:
                path: front-logout
                target: front-login
     access_control:

        ...
        - { path: ^/ui/logout, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/ui/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/ui, roles: IS_AUTHENTICATED_FULLY }