使用NodeJS进行Active Directory身份验证

23

我正在尝试构建一个NodeJS服务器,并计划使用组织的Microsoft Active Directory进行身份验证。

我尝试了许多包(如activedirectory、activedirectory2、ldapjs等),但似乎没有一个适合我。

我提供了LDAP URL,以下是我的代码。

var ldapjs = require('ldapjs');

var config = { url: 'ldap://mycompany.com/dc=mycompany,dc=com'
           ,timeout: 10
           ,reconnect: {
              "initialDelay": 100,
              "maxDelay": 500,
              "failAfter": 5
              } 
        }

var username = "user_id@mycompany.com";
var password="password";

const ldapClient = ldapjs.createClient(config);


ldapClient.bind(username, password, function (err) {
console.log("Logging data...");
ldapClient.search('dc=mycompany,dc=com', function (err, search) {
 if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }
search.on('searchEntry', function (err,entry) {
   if (err) {
    console.log('ERROR: ' +JSON.stringify(err));
    return;
  }
  else{
    var user = entry.object;
    console.log("Done.");
    return;
   }

   });
  });
});

有时它可以工作,但大部分时间我会不停地收到以下错误消息(可能是当它选择不同的IP时)。

有时它可以工作,但大部分时间我会不停地收到以下错误消息(可能是当它选择不同的IP时)

Error: connect ETIMEDOUT <ip address>:389
at Object.exports._errnoException (util.js:1018:11)
at exports._exceptionWithHostPort (util.js:1041:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1090:14)

让我感到困惑的是,如果我在我的C#应用程序中尝试使用相同的LDAP URL,它可以正常工作。

.Net应用程序使用方式和NodeJS使用方式有区别吗?

我能否通过更改代码使其正常工作?

6个回答

19
因为这是Google搜索结果中的第一个问题,并且我花费了相当长的时间才弄清楚如何使用Active Directory身份验证,所以我将分享来自这个教程的解决方案。
与我在互联网上找到的其他示例相比,它非常易于理解和实现: npm install --save activedirectory
// Initialize
var ActiveDirectory = require('activedirectory');
var config = {
    url: 'ldap://dc.domain.com',
    baseDN: 'dc=domain,dc=com'
};
var ad = new ActiveDirectory(config);
var username = 'john.smith@domain.com';
var password = 'password';
// Authenticate
ad.authenticate(username, password, function(err, auth) {
    if (err) {
        console.log('ERROR: '+JSON.stringify(err));
        return;
    }
    if (auth) {
        console.log('Authenticated!');
    }
    else {
        console.log('Authentication failed!');
    }
});

最困难的部分是确定用户名要使用什么后缀。

我一直在收到以下错误:

错误:{"lde_message":"80090308: LdapErr: DSID-0C090400, comment: AcceptSecurityContext 错误,数据52e,v1db1\u0000","lde_dn":null}

在最终设置正确的后缀之前,我的用户名大概是这样的:
var username = 'john.smith@foo.companyname.com


2
嗨,Alex。你的代码中的用户名和密码是什么?是由正在进行身份验证的用户提供的还是其他什么? - Himanshu Singh
1
@HimanshuSingh 是的,用户名和密码是由用户提供的。在我的代码中,它看起来像这样:exports.login = (username, password, next) => {ad.authenticate(username + "@,my.company.com", password, (err, auth) => {... - Alex Weitz
嗯...我想知道这是否可以称为SSO。当我设置与AD的SSO时,我的理解和期望完全不同。如果我的应用程序用户必须再次输入用户名和密码,那么我不确定这是否可以算作SSO。您能否看一下我发布的问题并给我一些见解?https://stackoverflow.com/questions/65936881/authentication-flow-for-sso-between-a-node-js-react-app-and-microsoft-active 非常感谢您的帮助 :) - Himanshu Singh
@HimanshuSingh,这并不安全,密码容易受到MIM和证书欺骗的攻击。一个安全的身份验证方式应该让用户直接向AD进行身份验证,授权应用程序,并将可撤销且过期的令牌传递给服务。 - Tamir Daniely
1
这对我有效。在LDAP URL中,IP地址也足够了。变量配置如下:{ url: 'ldap://ip地址' }; 用户名为'username@domain.local';密码为'PASSWORD'。 - Raju
显示剩余2条评论

4

首先使用npm:express-ntlm获取发出请求的用户名,然后利用这些信息,使用npm:activedirectory查询该用户在Active Directory中的详细信息。

app.use(
  ntlm({
    domain: process.env.DOMAIN,
    domaincontroller: process.env.DOMAINCONTROLLER
  })
);

...

app.use("/", authenticate, require("./routes/index"));

在我的身份验证中间件中,我现在可以访问req.ntlm,其中包含

{ DomainName: '...',
  UserName: '...',
  Workstation: '...',
  Authenticated: true }

我设置了ActiveDirectory对象,并注意到使用"bindDN"和"bindCredentials"而不是"username"和"password":

var ad = new ActiveDirectory({
  url: process.env.DOMAINCONTROLLER,
  baseDN: process.env.BASEDN,
  bindDN: process.env.USERNAME,
  bindCredentials: process.env.PASSWORD
});

然后您可以像npm:activedirectory文档中那样使用广告对象:
ad.findUser(req.ntlm.UserName, (err, adUser) => {
    ...
});

findUser返回的内容包括名字、姓氏和电子邮件地址,这正是我所需要的,但你也可以轻松查看用户组。


可能需要指出的是,绑定用户不是登录的用户,而是具有权限进行所需成员查询的服务用户。 - Tamir Daniely
即使如今仍然被认为是脆弱的,但OAuth / SAML将提供更好的安全性。 - Tamir Daniely

3
配置对象指定了10毫秒的超时时间。这似乎相当短。你是想要一个10秒的超时时间吗? JS文档 你在C#中使用LdapConnection.Timeout对象吗?那个需要秒数。 C#文档

0
var ActiveDirectory = require('activedirectory');
        
var username = 'uid='+req.body.username+',cn=users,dc=domain,dc=com';
var password = req.body.password;
    
var ad = new ActiveDirectory({
  "url": "ldap://domain.com",
  "baseDN": "dc=domain,dc=com"
});
    
ad.authenticate(username, password, function (err, auth) {
  if (err) {
    res.send({ 
      status:404,
      'msg':'Wrong Credential!!',
      'data':false
    })
  }
  if (auth) {
    console.log('Authenticated from Active directory!');
    res.send({ 
      status:200,
      'msg':'Success',
      'data':true
    })
  }
});

用户名需要与基本DN绑定。 例如:='uid=john,cn=users,dc=domain,dc=com'

它适用于NodeJS版本10以上。 Active Directory包不支持节点版本<= 9。


0
我们可以使用bind方法来进行用户身份验证,具体如下。
/*update the url according to your ldap address*/
var client = ldap.createClient({
    url: "ldaps://127.0.0.1:636",
    tlsOptions: {
      ca: [
        fs.readFileSync("./chain.pem")
      ],
    },
  });

/*use this to create connection*/
function authenticateDN(username, password) {

    /*bind use for authentication*/
    client.bind(username, password, function (err) {
        if (err) {
            console.log("Error in new connetion " + err)
        } else {
            /*if connection is success then go for any operation*/
            console.log("Success");
        }
    });
}

/*create authentication*/
authenticateDN("uid=admin,ou=system", "secret")

-1

我猜测你遇到的问题可能是因为活动目录(AD)高度可用,而不总是100%的域控制器在线。C#似乎有一些方法可以检测哪些不在线?

当我使用HA名称作为域名时,我也有类似的经历。

在这些情况下,最好与您的AD管理员交谈,并获取应该为您的服务器提供服务的AD服务器,并通过IP地址或DNS直接与其通信。

希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接