Java: 使用 COM4J 从 Active Directory 获取用户的信息,例如电子邮件和职位





5.00/5 (6投票s)
本文展示了 Java 应用程序如何从 Active Directory 检索用户数据。
引言
本文档简要介绍了如何使用 COM4J 从 Active Directory 获取“扩展”的用户数据。
动机
我在实施 SSO(单点登录)项目时遇到了这个问题。
使用 Java 实现基于 AD 的 SSO 比使用“原生”语言(如 C++)要复杂得多。即使 C#,虽然不被认为是“原生”语言,也提供了更好的机会。幸运的是,像 Waffle 这样的项目使生活变得更加轻松。然而,即使 Waffle 在很多方面都有帮助,它也有其局限性。例如,它无法从 AD 检索所有所需参数。Waffle 所做的是,它实现了本地机器的 Windows 和 Active Directory 之间的协商,从而执行 SSO 机制。用户身份验证完成后,如果您想从 AD 检索有关此用户的更多详细信息怎么办?例如,如果您想从 AD 获取用户的电子邮件、电话、地址等 - 使用 Waffle,这是不可能的。
再次幸运的是,有一些由 Java 包装的本机工具,例如 COM4J。
在下面的代码中,我将展示如何使用 COM4J 实现此目的。
代码
如上所述,可以通过本机调用检索来自 AD 的附加信息。COM4J 是一个不错的选择。如果您想使用 COM4J,您需要将几个 JAR 文件添加到您的依赖项中:ado20-1.0.jar、active-directory-1.0.jar 和 com4j-20110320.jar。如果您足够聪明并且使用 Maven,请将以下内容添加到您的 pom.xml 中
<dependency>
<groupId>org.jvnet.com4j</groupId>
<artifactId>com4j</artifactId>
<version>20110320</version>
</dependency>
<dependency>
<groupId>org.jvnet.com4j.typelibs</groupId>
<artifactId>active-directory</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.jvnet.com4j.typelibs</groupId>
<artifactId>ado20</artifactId>
<version>1.0</version>
</dependency>
代码的主要部分,也是最有趣的部分,是类 ActiveDirectoryUserInfo
的构造函数。在这里,我们访问 AD 并获取相关数据。请注意,构造函数是 private
,因为它是一个单例。
private ActiveDirectoryUserInfo (String username, String requestedInfo)
{
infoMap.clear();
initNamingContext();
if (defaultNamingContext == null) {
return;
}
//Searching LDAP requires ADO,so it's good to create a connection upfront for reuse.
_Connection con = ClassFactory.createConnection();
con.provider("ADsDSOObject");
con.open("Active Directory Provider",""/*default*/,""/*default*/,-1/*default*/);
// query LDAP to find out the LDAP DN and other info for the given user from the login ID
_Command cmd = ClassFactory.createCommand();
cmd.activeConnection(con);
String searchField = "userPrincipalName";
int pSlash = username.indexOf('\\');
if (pSlash > 0)
{
searchField = "sAMAccountName";
username = username.substring(pSlash+1);
}
cmd.commandText("<LDAP://"+defaultNamingContext+">;("+searchField+"="+username+
");"+requestedInfo+";subTree");
_Recordset rs = cmd.execute(null, Variant.getMissing(), -1/*default*/);
if(rs.eof())
{
// User not found!
_log.error(username+" not found.");
}
else
{
Fields userData = rs.fields();
if (userData != null)
{
//see below: we build the map of requested-info:
buildInfoMap(requestedInfo, userData);
}
else
{
_log.error("User "+username+" information is empty.");
}
}
if(infoMap.isEmpty())
{
_log.error("user-info map is empty - no data was written to it.");
}
rs.close();
con.close();
}
构建 InfoMap
private void buildInfoMap(String requestedInfo, Fields userData)
{
StringTokenizer tokenizer = new StringTokenizer(requestedInfo, ",");
String detail ;
String value = null;
while( tokenizer.hasMoreTokens() )
{
detail = tokenizer.nextToken();
try
{
Object o = userData.item(detail).value();
if (o != null)
{
value = o.toString();
_log.info(detail + " = " + value);
infoMap.put(detail, value);
}
}
catch (ComException ecom )
{
_log.error(detail + " not returned: "+ecom.getMessage());
}
}
}
初始化命名上下文
/**
* "LDAP://RootDSE" connects the active directory that the local machine is connected to.
* if we want to support cases of multiple domains, and enable connection from one domain to
* another, we should pass the DSE as a param.
* @param domainServerAddress
*/
synchronized void initNamingContext(String domainServerAddress)
{
_log.debug("* initNamingContext *, domainServerAddress= " + domainServerAddress);
if (defaultNamingContext == null)
{
if(domainServerAddress == null || domainServerAddress.isEmpty())
{
domainServerAddress = "RootDSE";
}
IADs rootDSE = COM4J.getObject(IADs.class, "LDAP://" + domainServerAddress, null);
defaultNamingContext = (String)rootDSE.get("defaultNamingContext");
_log.info("defaultNamingContext= " + defaultNamingContext);
}
}
Using the Code
要使用此类,客户端应用程序必须提供两件事:用户在域中的完全限定名称,以及包含 AD 中所有感兴趣字段的 string
(逗号分隔)。这些 param
s 传递给“getInstance
”方法,从而创建一个实例。然后,所有需要做的是调用 map 的 getter,并获取所需的信息。
在下面的示例中,我们对用户的电子邮件、电话和其他参数感兴趣。用户的 FQN 是“john\doe”,这意味着域名是“john
”,用户名是“doe
”。
String requestedFields= "distinguishedName,userPrincipalName,telephoneNumber,mail”;
//the fully qualified name of the user in the AD. <Domain-name>\<username>
String fqn = "john\doe”;
ActiveDirectoryUserInfo userInfo = ActiveDirectoryUserInfo.getInstance(fqn, requestedFields);
Map<String, String> infoMap = userInfo.getInfoMap();
String email = infoMap.get("mail");
请注意,不应像本示例中描述的那样对任何内容进行硬编码;这仅用于演示目的。
贷方
感谢 Christophe Dupriez 的大力支持,他在我努力解决这个挑战时给予了我帮助和指导。