node.js 节点 JS LDAP 身份验证用户

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17795007/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 15:06:15  来源:igfitidea点击:

Node JS LDAP Auth User

node.jsauthenticationldap

提问by Sukh

I am creating a login authentication page, where a user would input there active directory username and password and using NodeJS I would check to see if it's valid, but I keep getting

我正在创建一个登录身份验证页面,用户将在其中输入活动目录用户名和密码并使用 NodeJS 我会检查它是否有效,但我不断收到

[Error: LDAP Error Bad search filter]

or

或者

[Error: Search returned != 1 results]

When I'm trying to search for the username and password, my code is below:

当我尝试搜索用户名和密码时,我的代码如下:

I'm using: https://github.com/jeremycx/node-LDAP, let's say that the user entered a username of hhill

我正在使用:https://github.com/jeremycx/node-LDAP,假设用户输入了 hhill 的用户名

    var ldap = require('LDAP');
    var ldapServer = new ldap({ uri: 'ldap://batman.lan', version: 3});

    ldapServer.open(function(error) {
        if(error) {
           throw new Error('Cant not connect');
        } else {
            console.log('---- connected to ldap ----');

            username = '(cn='+username+')';
            ldapServer.findandbind({
                base: 'ou=users,ou=compton,dc=batman,dc=lan',
                filter: username,
                password: password
            }, function(error, data) {
                if(error){
                    console.log(error);
                } else {
                    console.log('---- verified user ----');
                }
            });
        }
    });

Does anyone have any suggestions on what I'm doing wrong?

有没有人对我做错了什么有任何建议?

UPDATE

更新

Here is the solution I came up with if anyone ever needs it, with the help of the answer below

这是我想出的解决方案,如果有人需要的话,在下面的答案的帮助下

    var username = request.param('username');
    var password = request.param('password');

    var ldap = require('ldapjs');
    ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
    var client = ldap.createClient({
          url: 'ldap://batman.com/cn='+username+', ou=users, ou=compton, dc=batman, dc=com',
          timeout: 5000,
          connectTimeout: 10000
    });
    var opts = {
      filter: '(&(objectclass=user)(samaccountname='+username+'))',
      scope: 'sub',
      attributes: ['objectGUID']
    };

    console.log('--- going to try to connect user ---');

    try {
        client.bind(username, password, function (error) {
            if(error){
                console.log(error.message);
                client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
            } else {
                console.log('connected');
                client.search('ou=users, ou=compton, dc=batman, dc=com', opts, function(error, search) {
                    console.log('Searching.....');

                    search.on('searchEntry', function(entry) {
                        if(entry.object){
                            console.log('entry: %j ' + JSON.stringify(entry.object));
                        }
                    });

                    search.on('error', function(error) {
                        console.error('error: ' + error.message);
                    });

                    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
                });
            }
        });
    } catch(error){
        console.log(error);
        client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
    }

采纳答案by zs2020

In this case, you need ldapClientrather than ldapServer, this is the example code from the official doc:

在这种情况下,您需要ldapClient而不是ldapServer,这是来自官方文档的示例代码:

var ldap = require('ldapjs');

ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;

var client = ldap.createClient({
  url: 'ldap://127.0.0.1/CN=test,OU=Development,DC=Home'
});

var opts = {
  filter: '(objectclass=user)',
  scope: 'sub',
  attributes: ['objectGUID']
};

client.bind('username', 'password', function (err) {
  client.search('CN=test,OU=Development,DC=Home', opts, function (err, search) {
    search.on('searchEntry', function (entry) {
      var user = entry.object;
      console.log(user.objectGUID);
    });
  });
});

回答by Shaun

@Sukh Thank you for posting your UPDATE solution; however, there is a problem with the code you posted in your UPDATE. While it works for simple cases, with larger queries, you will find you are unbinding before the results have been output. The solution for me was to move your unbinds into the search.on functions.

@Sukh 感谢您发布更新解决方案;但是,您在 UPDATE 中发布的代码存在问题。虽然它适用于简单的情况,但对于较大的查询,您会发现在输出结果之前就已解除绑定。我的解决方案是将您的解除绑定移动到 search.on 函数中。

Here is an edit of your UPDATE:

这是您的 UPDATE 的编辑:

var ldap = require('ldapjs');
ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
var client = ldap.createClient({
      url: 'ldap://batman.com/cn='+username+', ou=users, ou=compton, dc=batman, dc=com',
      timeout: 5000,
      connectTimeout: 10000
});
var opts = {
  filter: '(&(objectclass=user)(samaccountname='+username+'))',
  scope: 'sub',
  //attributes: ['objectGUID']
  // This attribute list is what broke your solution
  attributes: ['objectGUID','sAMAccountName','cn','mail','manager','memberOf']
};

console.log('--- going to try to connect user ---');

try {
    client.bind(username, password, function (error) {
        if(error){
            console.log(error.message);
            client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
        } else {
            console.log('connected');
            client.search('ou=users, ou=compton, dc=batman, dc=com', opts, function(error, search) {
                console.log('Searching.....');

                search.on('searchEntry', function(entry) {
                    if(entry.object){
                        console.log('entry: %j ' + JSON.stringify(entry.object));
                    }
                    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
                });

                search.on('error', function(error) {
                    console.error('error: ' + error.message);
                    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
                });

                // don't do this here
                //client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
            });
        }
    });
} catch(error){
    console.log(error);
    client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
}

At least this is what I discovered when using your solution with Active Directory searches. memberOf returns A LOT of entries in my use case and the unbinds were being done prematurely, so I was getting the following error:

至少这是我在将您的解决方案与 Active Directory 搜索结合使用时所发现的。memberOf 在我的用例中返回了很多条目,并且解除绑定过早完成,所以我收到以下错误:

error: 1__ldap://my.domain.com/,OU=Employees,OU=Accounts,DC=my,DC=domain,DC=com closed
client disconnected

回答by Rohit Parte

Suggestions

建议

1.Don't use ldapauth-fork (Huge hanging issue, if we hit multiple requests then after some time library gets unresponsive and doesn't return anything.)

1.不要使用 ldapauth-fork(巨大的悬而未决的问题,如果我们遇到多个请求,那么一段时间后库会变得无响应并且不返回任何内容。)

2.Don't use passport-ldapauth (Internally calls ldapauth-fork)

2.不要使用passport-ldapauth(内部调用ldapauth-fork)

We can use ldapjs, which has easy implementation and is based on event driven approach.

我们可以使用ldapjs,它易于实现并且基于事件驱动的方法。

Below nodejs code explain complete solution for ldap auth and search.

下面的 nodejs 代码解释了 ldap 身份验证和搜索的完整解决方案。

JS code

JS代码

const ldap = require('ldapjs');
let client

// unbind after completion of process
function closeConnection() {
    console.log('closeConnection')
    client.unbind(err => {
        console.log('unbind error', err)
    });
}

function search() {
    const searchOptions = {
        filter: '(uid=yourSearchText)', // search text
        scope: 'sub'
    };
    return new Promise((resolve, reject) => {
        client.search('ou=consultants,' + 'ou="Your OU",ou=yourOu,dc=yourDc,dc=com', searchOptions, (err, res) => {
            res.on('searchEntry', entry => {
                console.log('searchEntry', entry.object);
                resolve(entry.object)
            });
            res.on('searchReference', referral => {
                console.log('referral: ' + referral.uris.join());
                resolve(referral.uris.join())
            });
            res.on('error', err => {
                console.error('search error: ' + err.message);
                reject(err)
            });
            res.on('end', result => {
                console.log('If not found', result);
                reject({ message:'User not found'})
            });
        });
    })
}

function authenticate() {
    const server = 'ldap server ip';
    client = ldap.createClient({
        url: `ldap://${server}`
    });

    return new Promise((resolve, reject) => {
        client.bind('cn=yourcn,dc=yourdc,dc=com', 'sortedSolutions', err => {
            if (err) {
                reject(err)
            }
            resolve('Authenticated successfully')
        });
    })
}

function start(req, res) {
    let searchResponseData
    authenticate()
        .then(authenticateResponse => {
            console.log('authenticateResponse', authenticateResponse)
            return search()
        })
        .then(searchResponse => {
            console.log('searchResponsesearchResponse', searchResponse)
            searchResponseData = searchResponse
            return closeConnection()
        })
        .then(closeConnectionResponse => {
            console.log('ldap connection closed', closeConnectionResponse)
            res.status(200).send(searchResponseData)
        })
        .catch(error => {
            console.log('catch error', error)
            res.status(400).send(error)
        })

}

module.exports.start = start

// We can use same code with no authentication, Just pass '' to bind function client.bind('', '', err => { //same as above })

// 我们可以使用相同的代码而无需身份验证,只需通过 '' 来绑定函数 client.bind('', '', err => { // 同上 })