繁体   English   中英

使用fr3d ldapbundle&fos userbundle登录,找到了用户,但在数据库中插入用户名为null

[英]Login with fr3d ldapbundle & fos userbundle, user found but on DB insert username is null

(最后更新)

我无法从特定的LDAP注册任何用户,即使从LDAP找到该用户后,当它尝试注册时,用户名为null,因此会触发违规约束并回滚。 我正在使用Sonata用户捆绑包,FR3D LDAP捆绑包和FOS用户捆绑包。

开发日志

[2018-01-30 16:06:09] doctrine.DEBUG: SELECT t0.username AS username_1, t0.username_canonical AS username_canonical_2, t0.email AS email_3, t0.email_canonical AS email_canonical_4, t0.enabled AS enabled_5, t0.salt AS salt_6, t0.password AS password_7, t0.last_login AS last_login_8, t0.confirmation_token AS confirmation_token_9, t0.password_requested_at AS password_requested_at_10, t0.roles AS roles_11, t0.created_at AS created_at_12, t0.updated_at AS updated_at_13, t0.date_of_birth AS date_of_birth_14, t0.firstname AS firstname_15, t0.lastname AS lastname_16, t0.website AS website_17, t0.biography AS biography_18, t0.gender AS gender_19, t0.locale AS locale_20, t0.timezone AS timezone_21, t0.phone AS phone_22, t0.facebook_uid AS facebook_uid_23, t0.facebook_name AS facebook_name_24, t0.facebook_data AS facebook_data_25, t0.twitter_uid AS twitter_uid_26, t0.twitter_name AS twitter_name_27, t0.twitter_data AS twitter_data_28, t0.gplus_uid AS gplus_uid_29, t0.gplus_name AS gplus_name_30, t0.gplus_data AS gplus_data_31, t0.token AS token_32, t0.two_step_code AS two_step_code_33, t0.ab_number AS ab_number_34, t0.title AS title_35, t0.hire_date AS hire_date_36, t0.employment_status AS employment_status_37, t0.departure_date AS departure_date_38, t0.departure_reason AS departure_reason_39, t0.local_balance AS local_balance_40, t0.sick_balance AS sick_balance_41, t0.frozen_local_balance AS frozen_local_balance_42, t0.carry_forward_local_balance AS carry_forward_local_balance_43, t0.frozen_carry_forward_local_balance AS frozen_carry_forward_local_balance_44, t0.is_no_probation_leaves AS is_no_probation_leaves_45, t0.dn AS dn_46, t0.created_by AS created_by_47, t0.updated_by AS updated_by_48, t0.id AS id_49, t0.job_title_id AS job_title_id_50, t0.department_id AS department_id_51, t0.project_id AS project_id_52, t0.business_unit_id AS business_unit_id_53 FROM fos_user_user t0 WHERE t0.username_canonical = ? LIMIT 1 ["gpotest"] []
[2018-01-30 16:06:09] ldap_driver.DEBUG: ldap_search(dc=example,dc=mu, (&(&(ObjectClass=person))(sAMAccountName=gpotest)), [array]) {"action":"ldap_search","base_dn":"dc=example,dc=mu","filter":"(&(&(ObjectClass=person))(sAMAccountName=gpotest))","attributes":[]} []
[2018-01-30 16:06:09] security.INFO: User gpotest found on LDAP {"action":"loadUserByUsername","username":"gpotest","result":"found"} []
[2018-01-30 16:06:09] ldap_driver.DEBUG: ldap_bind(CN=gpotest,OU=Test,DC=example,DC=mu, ****) {"action":"ldap_bind","bind_rdn":"CN=gpotest,OU=Test,DC=example,DC=mu"} []
[2018-01-30 16:06:09] security.INFO: User has been authenticated successfully. {"username":null} []
[2018-01-30 16:06:09] doctrine.DEBUG: "START TRANSACTION" [] []
[2018-01-30 16:06:09] doctrine.DEBUG: INSERT INTO fos_user_user (username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, created_at, updated_at, date_of_birth, firstname, lastname, website, biography, gender, locale, timezone, phone, facebook_uid, facebook_name, facebook_data, twitter_uid, twitter_name, twitter_data, gplus_uid, gplus_name, gplus_data, token, two_step_code, ab_number, title, hire_date, employment_status, departure_date, departure_reason, local_balance, sick_balance, frozen_local_balance, carry_forward_local_balance, frozen_carry_forward_local_balance, is_no_probation_leaves, dn, created_by, updated_by, job_title_id, department_id, project_id, business_unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) {"1":null,"2":null,"3":null,"4":null,"5":true,"6":null,"7":"","8":"2018-01-30 16:06:09","9":null,"10":null,"11":["ROLE_EMPLOYEE"],"12":"2018-01-30 16:06:09","13":"2018-01-30 16:06:09","14":null,"15":null,"16":null,"17":null,"18":null,"19":"u","20":null,"21":null,"22":null,"23":null,"24":null,"25":null,"26":null,"27":null,"28":null,"29":null,"30":null,"31":null,"32":null,"33":null,"34":null,"35":null,"36":null,"37":null,"38":null,"39":null,"40":0,"41":0,"42":0,"43":0,"44":0,"45":false,"46":"CN=gpotest,OU=Test,DC=example,DC=mu","47":null,"48":null,"49":null,"50":null,"51":null,"52":null} []
[2018-01-30 16:06:09] doctrine.DEBUG: "ROLLBACK" [] []
[2018-01-30 16:06:09] request.CRITICAL: Uncaught PHP Exception Doctrine\DBAL\Exception\NotNullConstraintViolationException: "An exception occurred while executing 'INSERT INTO fos_user_user (username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, created_at, updated_at, date_of_birth, firstname, lastname, website, biography, gender, locale, timezone, phone, facebook_uid, facebook_name, facebook_data, twitter_uid, twitter_name, twitter_data, gplus_uid, gplus_name, gplus_data, token, two_step_code, ab_number, title, hire_date, employment_status, departure_date, departure_reason, local_balance, sick_balance, frozen_local_balance, carry_forward_local_balance, frozen_carry_forward_local_balance, is_no_probation_leaves, dn, created_by, updated_by, job_title_id, department_id, project_id, business_unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' with params [null, null, null, null, 1, null, "", "2018-01-30 16:06:09", null, null, "a:1:{i:0;s:13:\"ROLE_EMPLOYEE\";}", "2018-01-30 16:06:09", "2018-01-30 16:06:09", null, null, null, null, null, "u", null, null, null, null, null, "null", null, null, "null", null, null, "null", null, null, null, null, null, null, null, null, 0, 0, 0, 0, 0, 0, "CN=gpotest,OU=Test,DC=example,DC=mu", null, null, null, null, null, null]:  SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'username' cannot be null" at E:\wamp64\www\acshrm\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\AbstractMySQLDriver.php line 118 {"exception":"[object] (Doctrine\\DBAL\\Exception\\NotNullConstraintViolationException(code: 0): An exception occurred while executing 'INSERT INTO fos_user_user (username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, created_at, updated_at, date_of_birth, firstname, lastname, website, biography, gender, locale, timezone, phone, facebook_uid, facebook_name, facebook_data, twitter_uid, twitter_name, twitter_data, gplus_uid, gplus_name, gplus_data, token, two_step_code, ab_number, title, hire_date, employment_status, departure_date, departure_reason, local_balance, sick_balance, frozen_local_balance, carry_forward_local_balance, frozen_carry_forward_local_balance, is_no_probation_leaves, dn, created_by, updated_by, job_title_id, department_id, project_id, business_unit_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' with params [null, null, null, null, 1, null, \"\", \"2018-01-30 16:06:09\", null, null, \"a:1:{i:0;s:13:\\\"ROLE_EMPLOYEE\\\";}\", \"2018-01-30 16:06:09\", \"2018-01-30 16:06:09\", null, null, null, null, null, \"u\", null, null, null, null, null, \"null\", null, null, \"null\", null, null, \"null\", null, null, null, null, null, null, null, null, 0, 0, 0, 0, 0, 0, \"CN=gpotest,OU=Test,DC=example,DC=mu\", null, null, null, null, null, null]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1048 Column 'username' cannot be null at E:\\wamp64\\www\\acshrm\\vendor\\doctrine\\dbal\\lib\\Doctrine\\DBAL\\Driver\\AbstractMySQLDriver.php:118, Doctrine\\DBAL\\Driver\\PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'username' cannot be null at E:\\wamp64\\www\\acshrm\\vendor\\doctrine\\dbal\\lib\\Doctrine\\DBAL\\Driver\\PDOStatement.php:107, PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'username' cannot be null at E:\\wamp64\\www\\acshrm\\vendor\\doctrine\\dbal\\lib\\Doctrine\\DBAL\\Driver\\PDOStatement.php:105)"} []

如您所见,它说找到的用户使用正确的用户名:

在LDAP上找到的用户gpotest {“ action”:“ loadUserByUsername”, “ username”:“ gpotest” ,“ result”:“ found”}

但是然后绑定之后:

security.INFO:用户已成功通过身份验证。 { “ username”:null }

但是,通过使用下面config.yml中注释掉的Online LDAP服务器配置,我能够成功连接并进行身份验证:

[2018-01-24 23:46:03] ldap_driver.DEBUG: ldap_bind(uid=gauss,dc=example,dc=com, ****) {"action":"ldap_bind","bind_rdn":"uid=gauss,dc=example,dc=com"} []
[2018-01-24 23:46:03] doctrine.DEBUG: SELECT t0.name AS name_1, t0.roles AS roles_2, t0.id AS id_3 FROM fos_user_group t0 INNER JOIN fos_user_user_group ON t0.id = fos_user_user_group.group_id WHERE fos_user_user_group.user_id = ? [188] []
[2018-01-24 23:46:03] security.INFO: User has been authenticated successfully. {"username":"gauss"} []
[2018-01-24 23:46:03] doctrine.DEBUG: "START TRANSACTION" [] []
[2018-01-24 23:46:03] doctrine.DEBUG: UPDATE fos_user_user SET last_login = ?, updated_at = ? WHERE id = ? ["2018-01-24 23:46:03","2018-01-24 23:46:03",188] []
[2018-01-24 23:46:03] doctrine.DEBUG: "COMMIT" [] []
[2018-01-24 23:46:03] security.DEBUG: Stored the security token in the session. {"key":"_security_user"} []

这是我相关的文件内容:

composer.json

"fr3d/ldap-bundle": "^3.0",
"friendsofsymfony/user-bundle": "^2.0",
"sonata-project/admin-bundle": "^3.23",
"sonata-project/doctrine-orm-admin-bundle": "^3.1",
"sonata-project/user-bundle": "^4.0",
"symfony/symfony": "3.4.*",

配置文件

fr3d_ldap:
    # driver:
        # host: ldap.forumsys.com
    # user:
        # baseDn: 'dc=example,dc=com'
        # attributes:
            # - { ldap_attr: uid,  user_method: setUsername }
            # - { ldap_attr: mail,  user_method: setEmail }
            # - { ldap_attr: cn,  user_method: setFirstname }
            # - { ldap_attr: sn,  user_method: setLastname }
        # filter: (&(ObjectClass=person))

    driver:
        host: example.mu
        accountFilterFormat: (&('sAMAccountName'=%s))
        username: gpotest
        password: apassword
        bindRequiresDn: false
    user:      
        baseDn: 'dc=example,dc=mu'
        attributes:
            - { ldap_attr: sAMAccountName,  user_method: setUsername }
            - { ldap_attr: mail,  user_method: setEmail }
            # - { ldap_attr: cn,  user_method: setFirstname }
            - { ldap_attr: sn,  user_method: setLastname }
        filter: (&(ObjectClass=person))
        usernameAttribute: 'sAMAccountName'

# Sonata User Bundle
sonata_user:
    security_acl: false
    manager_type: orm # can be orm or mongodb
    class:
        user: Application\Sonata\UserBundle\Entity\User
        group: Application\Sonata\UserBundle\Entity\Group
    admin:                  # Admin Classes
        user:
            class:          LeavesOvertimeBundle\Admin\UserAdmin
            controller:     SonataAdminBundle:CRUD
            translation:    SonataUserBundle

fos_user:
    db_driver:      orm # can be orm or odm
    firewall_name:  main
    user_class:     Application\Sonata\UserBundle\Entity\User #Sonata\UserBundle\Entity\BaseUser

    group:
        group_class:   Application\Sonata\UserBundle\Entity\Group #Sonata\UserBundle\Entity\BaseGroup
        group_manager: sonata.user.orm.group_manager

    service:
        user_manager: sonata.user.orm.user_manager

    from_email:
        address:        '%from_email%'
        sender_name:    AS 

安全性

security:
    erase_credentials: false
    # Sonata User Bundle
    encoders:
        FOS\UserBundle\Model\UserInterface: bcrypt
#    acl:
#        connection: default

    role_hierarchy:
        ROLE_USER:
            - ROLE_SONATA_ADMIN
        ROLE_EMPLOYEE:
            - ROLE_USER
        SONATA:
            - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT  # if you are using acl then this line must be commented

    access_decision_manager:
        strategy: unanimous

    providers:
        chain_provider:
            chain:
                providers: [fos_userbundle, fr3d_ldapbundle]
        fr3d_ldapbundle:
            id: fr3d_ldap.security.user.provider
        fos_userbundle:
            id: fos_user.user_provider.username

    firewalls:
        # Disabling the security for the web debug toolbar, the profiler and Assetic.
        dev:
            pattern:  ^/(_(profiler|wdt)|css|images|js)/
            security: false

        # -> custom firewall for the admin area of the URL
        admin:
            switch_user:        true
            pattern:            /admin(.*)
            fr3d_ldap:          ~
            context:            user
            form_login:
                provider:       fos_userbundle # - looking back at this code, this was probably an issue, but I remember trying combination of every provider I have above. In my final solution this must be removed so that it takes the chain provider
                login_path:     /admin/login
                use_forward:    false
                check_path:     /admin/login_check
                failure_path:   null
                default_target_path: /admin/dashboard
                always_use_default_target_path: true
            logout:
                path:           /admin/logout
                target:         /admin/login
            anonymous:          true

        main:
            switch_user:        true
            fr3d_ldap:          ~
            pattern:             .*
            context:             user
            form_login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                failure_path:   null
            logout:             true
            anonymous:          true 

用户类别:(我已删除了irrelevent字段)

namespace Application\Sonata\UserBundle\Entity;

use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use FR3D\LdapBundle\Model\LdapUserInterface;

class User extends BaseUser implements LdapUserInterface
{

    /**
     * @var int $id
     */
    protected $id;

    /**
     * Get id
     *
     * @return int $id
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @var string|null
     */
    protected $dn;

    public function __construct()
    {
        parent::__construct();
        if (empty($this->roles)) {
            $this->roles[] = 'ROLE_EMPLOYEE';
        }
    }

    /**
     * @param mixed $dn
     *
     * @return User
     */
    public function setDn($dn) {
        $this->dn = $dn;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getDn() {
        return $this->dn;
    }

上面的扩展类基本上导致FOSUserBundle的用户名属性为User类https://github.com/sonata-project/SonataUserBundle/blob/4.x/src/Entity/BaseUser.php https://github.com/sonata-project /SonataUserBundle/blob/4.x/src/Model/User.php https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Model/User.php

覆盖的user.orm.xml :(我删除了irrelevent字段)

<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
                  http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">

    <entity name="Application\Sonata\UserBundle\Entity\User" table="fos_user_user" repository-class="MyBundle\Repository\UserRepository">

        <id name="id" column="id" type="integer">
            <generator strategy="AUTO" />
        </id>
        <field name="dn" column="dn" type="string" length="255" nullable="true" />

    </entity>

</doctrine-mapping>

父级定义: https : //github.com/sonata-project/SonataUserBundle/blob/4.x/src/Resources/config/doctrine/BaseUser.orm.xml

更新1 :仍然无法使它起作用,因此我们尝试将'mail'替换为samaccountname,并且,我不知道为什么在调试过程中会影响我的令牌,但是它起作用了。 电子邮件正在用作用户名。 注销并尝试与另一台设备一起使用,它可以正常工作。 奇怪的是,在我尝试添加要保存的参数之后,以用户名身份发送的邮件停止了工作。 这次,当我进行另一个人测试时,他的电子邮件不起作用,但他的samaccountname(我一直试图进行的工作)起作用了……此时,我不知道到底发生了什么。

更新2这是使用邮件代替setUsername方法的sAMAccountName的日志(此后它将正确插入DB中)。 另一个奇怪的事情是给定name属性,其中包含firstName不会返回任何内容,但是使用MS的AD资源管理器,我可以看到它具有firstName。

[2018-02-05 11:42:51] ldap_driver.DEBUG: ldap_search(dc=example,dc=mu, (&(sAMAccountName=leaves)), [array]) {"action":"ldap_search","base_dn":"dc=example,dc=mu","filter":"(&(sAMAccountName=leaves))","attributes":[]} []
[2018-02-05 11:42:51] security.INFO: User leaves found on LDAP {"action":"loadUserByUsername","username":"leaves","result":"found"} []
[2018-02-05 11:42:51] ldap_driver.DEBUG: ldap_bind(CN=Leaves_Test HRM,OU=06 IT,DC=example,DC=mu, ****) {"action":"ldap_bind","bind_rdn":"CN=Leaves_Test HRM,OU=06 IT,DC=example,DC=mu"} []
[2018-02-05 11:42:51] security.INFO: User has been authenticated successfully. {"username":"Leaves_Test.HRM@email.com"} []

和config.yml:

driver:
    host: example.mu
#        accountDomainName: example
    accountDomainNameShort: example
#        accountFilterFormat: (&('sAMAccountName'=%s))
    username: leaves
    password: P@55w0rd
user:      
    baseDn: '%base_dn%'
    attributes:
        - { ldap_attr: mail,  user_method: setUsername }
        - { ldap_attr: mail,  user_method: setEmail }
        - { ldap_attr: cn,  user_method: setFirstname }
        - { ldap_attr: sn,  user_method: setLastname }
#        filter: (&(ObjectClass=person))
    usernameAttribute: 'sAMAccountName'

总而言之,我感到在检索信息时,我只能提取mail,cn,sn属性。 另外,请参阅日志的第一行,在该行的末尾有属性“:[]。为空是否正常?

我的问题是,与MS Active Directory绑定后,我只收到电子邮件,sn,cn信息,甚至没有samaccountname存储在我的User对象中。

解决方案是使用捆绑软件的自定义水化器:

https://github.com/Maks3w/FR3DLdapBundle/blob/master/Resources/doc/cookbook/custom_hydrator.md 通过此操作,您将可以访问搜索的LDAP记录中的每个属性。 如果您遵循文档说明,则仅需解决上述问题。

但是,在我的版本中,我通过CSV导入器导入了现有用户,该导入器包括来自LDAP的用户名,但是我无法自动生成其DN字符串,因此无法仅使用其用户名登录,因为其DN字符串包含特定的“ OU”信息。

我通过在每次登录时从LDAP加载用户来解决此问题,以获取DN字符串等经常更改的最新信息。 然后,我要么更新现有用户,要么根据存储库搜索结果创建一个新用户,这也允许导入的用户登录并且没有重复的用户条目。 感谢ejkun提供的优雅解决方案: https : //github.com/Maks3w/FR3DLdapBundle/issues/53#issuecomment-330905167 关于如何检测AD中的禁用帐户,还有一个好处。

这是我的UserHydrator版本:

use Doctrine\ORM\EntityManager;
use FR3D\LdapBundle\Hydrator\HydratorInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Application\Sonata\UserBundle\Entity\User;

class UserHydrator implements HydratorInterface
{
    protected $entityManager;

    public function __construct(EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * Populate a user with the data retrieved from LDAP.
     *
     * @param array $ldapEntry LDAP result information as a multi-dimensional array.
     *              see {@link http://www.php.net/function.ldap-get-entries} for array format examples.
     *
     * @return UserInterface
     */
    public function hydrate(array $ldapEntry)
    {
        if (!$ldapEntry) {
            return null;
        }

        if (!array_key_exists('samaccountname', $ldapEntry)) {
            return null;
        }

        $username = $ldapEntry['samaccountname'][0];
        $userFromDB = $this->entityManager->getRepository('ApplicationSonataUserBundle:User')->findOneBy(['username' => $username]);
        if ($userFromDB == null) {
            $user = new User();
            $user->setPassword('');
        }
        else {
            $user = $userFromDB;
        }

        // These are basically if statements without else in a short form, but do not call set methods with null
        array_key_exists('mail', $ldapEntry) ? $user->setEmail($ldapEntry['mail'][0]) : null;
        array_key_exists('givenname', $ldapEntry) ? $user->setFirstName($ldapEntry['givenname'][0]) : null;
        array_key_exists('sn', $ldapEntry) ? $user->setLastName($ldapEntry['sn'][0]) : null;
        array_key_exists('distinguishedname', $ldapEntry) ? $user->setDn($ldapEntry['distinguishedname'][0]) : null;
        /**
         * 512 = Enabled
         * 514 = Disabled
         * 66048 = Enabled, password never expires
         * 66050 = Disabled, password never expires
         */
        $user->setEnabled(array_key_exists('useraccountcontrol', $ldapEntry) ?
            $ldapEntry['useraccountcontrol'][0] == 512 || 66048 ? true : false : true);
        $user->setUsername($username);

        return $user;
    }
}

Security.yml,颠倒链提供商的顺序:

providers:
    chain_provider:
        chain:
            providers: [fr3d_ldapbundle, fos_userbundle]
    fr3d_ldapbundle:
        id: fr3d_ldap.security.user.provider
    fos_userbundle:
        id: fos_user.user_provider.username

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM