使用Java从Active Directory获取用户信息

在实现单点登录(SSO)项目的过程中,需要处理与Active Directory相关的用户数据。使用Java实现SSO比使用C++等“原生”语言要复杂得多。尽管C#也不是“原生”语言,但它提供了更好的机会。幸运的是,像Waffle这样的项目让生活变得更轻松。然而,即使Waffle在许多方面都有所帮助,它也有局限性。例如,它无法从AD检索所有所需的参数。Waffle所做的是实现本地计算机与活动目录之间的协商,从而执行SSO机制。一旦用户通过身份验证,如果想从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 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", "", "", -1); // 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(";(" + searchField + "=" + username + ");" + requestedInfo + ";subTree"); _Recordset rs = cmd.execute(null, Variant.getMissing(), -1); 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(); }

构建信息映射:

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()); } } }

初始化命名上下文:

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); } }

使用代码

要使用这个类,客户端应用程序需要提供两件事:域中用户的完全限定名,以及它感兴趣的AD中所有字段的字符串(用逗号分隔)。这些参数传递给"getInstance"方法,从而创建一个实例。然后,所需要做的就是调用映射的getter,并获取所需的信息。

在下面的例子中,对用户的电子邮件、电话号码和其他参数感兴趣。用户的FQN是"john\doe",意味着域名是"john",用户名是"doe"。

String requestedFields = "distinguishedName,userPrincipalName,telephoneNumber,mail"; // the fully qualified name of the user in the AD. \ String fqn = "john\\doe"; ActiveDirectoryUserInfo userInfo = ActiveDirectoryUserInfo.getInstance(fqn, requestedFields); Map infoMap = userInfo.getInfoMap(); String email = infoMap.get("mail");
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485