使用js对接ldap

100 阅读1分钟
  • demo
import ldap from 'ldapjs';

// DC的网络路径
const server = '10.1.10.1';
// userPrincipalName 带邮箱后缀的 登录名
const userPrincipalName = 'administrator@abc.com';
const password = 'password';

const authentication = (userPrincipalName, password) => {
  return new Promise((resolve, reject) => {
    // DC的目录
    const adSuffix = 'DC=abc,DC=com';

    const success = (userInfo) => {
      client.unbind();
      resolve(userInfo);
    };

    const fail = (e) => {
      client.unbind();
      reject(e?.message ?? e);
    };

    // 建立连接,默认使用端口389
    const client = ldap.createClient({
      url: `ldap://${server}`,
    });
    // 鉴权
    client.bind(userPrincipalName, password, (error, res) => {
      if (error) {
        return fail(error);
      }
    });

    // 用户查询条件
    const searchOptions = {
      scope: 'sub',
      filter: `(userPrincipalName=${userPrincipalName})`,
      attributes: [
        'displayName',
        'name',
        'sAMAccountName',
        'userPrincipalName',
        'objectGUID',
        'objectSid',
        'accountExpires',
      ],
    };

    // 查询用户信息
    client.search(adSuffix, searchOptions, (error, res) => {
      if (error) {
        return fail(error);
      }

      res.on('searchEntry', (entry) => {
        const userInfo = entry.object;
        userInfo.objectGUID = formatGUID(entry);
        userInfo.accountExpires =
          Math.floor(userInfo.accountExpires / 10000) - 11644473600000;
        if (userInfo.accountExpires < Date.now()) {
          return fail('用户已禁用');
        }
        return success(userInfo);
      });
      res.on('error', (error) => {
        return fail(error);
      });
      res.on('end', () => {});
    });
  });
};

function formatGUID(entry){
  if(!Array.isArray(entry.attributes))
      throw new Error('Attributes must be an array');

  const binaryGUID = entry.attributes.find(attribute => attribute.type === 'objectGUID').buffers[0];
  const guidFormat = [
      [3,2,1,0],
      [5,4],
      [7,6],
      [8,9],
      [10,11,12,13,14,15]
  ];

  const guidArray = guidFormat.map( part => {
      const stringPart = part.map(byte => {
          const byteString = binaryGUID[byte] < 16 ?
              `0${binaryGUID[byte].toString(16)}` :
              binaryGUID[byte].toString(16)

          return byteString
      });
      return `${stringPart.join('')}`;
  });
  return guidArray.join('-');
}

const index = async () => {
  try {
    const userInfo = await authentication(userPrincipalName, password);
    console.log('userInfo', userInfo);
  } catch (e) {
    console.log('index:e', e);
  }
};
index();
{
  "dn": "CN=Admin,OU=Users,DC=abc,DC=com",
  "controls": [],
  "displayName": "Admin",
  "name": "Admin",
  "objectGUID": "��!����A��8�ŢZ�",
  "objectSid": "\u0001\u0002\u0000\u0000\u0000\u0000\u0000\u0003\u0012\u0000\u0000\u0000\u0011L�q�d�]\u000e\u0012C֖\u0012\u0000\u0000",
  "accountExpires": "9223372036854775807",
  "sAMAccountName": "admin",
  "userPrincipalName": "administrator@abc.com"
}
  • 登录鉴权的几种方式
const client = ldap.createClient({
  url: `ldap://${server}`,
});
const password = '';

// bind to AD by userPrincipalName
const userPrincipalName = 'admin@abc.com';
client.bind(userPrincipalName, password, (err, res) => {
  // ...
});

// bind to AD by CN(DN)
const CN = 'CN=Admin,OU=Users,DC=abc,DC=com';
client.bind(CN, password, (err, res) => {
  // ...
});

// bind to AD by sAMAccountName?
// 使用任意已知账号如管理员账号进行bind操作,
// bind成功后通过search操作以传入的sAMAccountName为搜索条件进行搜索
// 从搜索结果拿到userPrincipalName或DN后进行登录鉴权操作