Click here to Skip to main content
Click here to Skip to main content

Tagged as

Go to top

Java: Retrieving User's Information, Such As Email and Position, from Active Directory Using COM4J

, 3 Jul 2013
Rate this:
Please Sign up or sign in to vote.
This article shows how Java application can retrieve user's data from the Active Directory

Introduction

This short document describes the usage of COM4J in order to get "extended” user’s data from the Active Directory.

Motivation

I had to deal with this subject while implementing an SSO (single sign on) project.

Using Java, implementing SSO over an AD is much more cumbersome than in "native" languages such as C++. Even C#, which is not considered as a "native” language, offers better opportunities. Luckily, projects such as Waffle make life much easier. However though, even Waffle, that helps in many ways, has its limitations. For example, it cannot retrieve all desired parameters from the AD. What Waffle does do, it implements the negotiation between Windows of the local machine and the active directory, hence performs the SSO mechanism. Once the user is authenticated, what happens if you want to retrieve more details about this user from the AD? For example, if you want to get the user's email, telephone, address, etc. from the AD – using Waffle, it is simply impossible.

Lucky again, there are native tools that are wrapped by Java, such as COM4J.

In the code below, I will show how to use COM4J for such a purpose.

The Code

As the above, additional information from the AD can be retrieved by native calls. COM4J is a good option. If you want to use COM4J, you will have to add a few JARS to your dependencies: ado20-1.0.jar, active-directory-1.0.jar, and com4j-20110320.jar. If you are smart and work with Maven, add the following to your 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>

The main part of code, and most interesting, is the constructor of the class ActiveDirectoryUserInfo. Here, we go to the AD and fetch the relevant data. Note that the constructor is private since it is singleton.

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

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

Initializing the Naming Context

/**
* "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

To use this class, the client-application has to supply two things: the fully-qualified-name of the user in the domain, and the string with all fields in the AD that it is interested in (comma separated). These params are passed to the "getInstance” method, thus creating an instance. Then, all needs to be done is a call to the getter of the map, and get the desired information.

In the example below, we are interested in the users’ email, telephone and other parameters. The FQN of the user is "john\doe”, meaning the domain name is "john” and the user name is "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");

Note that nothing should be hard-coded as described in this example; this is only for demonstration purposes.

Credit

Big credit to Christophe Dupriez, who gave me a hand and a guiding light while I was struggling with this challenge.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Ohad Redlich

Israel Israel
worked for NICE systems (MFC C++ COM...)
worked for BMC software (Java, ...)
working for WatchDox (Java, Spring, Spring-Security...)
 
My Linkedin Profile
 
Visit my photography gallery

Comments and Discussions

 
QuestionNot working on JBoss AS 6 PinmemberMember 1023332326-Aug-13 9:56 
AnswerRe: Not working on JBoss AS 6 PinmemberOhad Redlich5-Sep-13 8:17 
GeneralRe: Not working on JBoss AS 6 PinmemberMember 102333236-Sep-13 1:27 
GeneralRe: Not working on JBoss AS 6 PinmemberOhad Redlich6-Sep-13 3:08 
GeneralRe: Not working on JBoss AS 6 PinmemberMember 102333236-Sep-13 3:35 
GeneralRe: Not working on JBoss AS 6 [modified] PinmemberOhad Redlich6-Sep-13 7:47 
GeneralRe: Not working on JBoss AS 6 PinmemberMember 102333239-Sep-13 2:00 
GeneralRe: Not working on JBoss AS 6 PinmemberOhad Redlich9-Sep-13 2:06 
GeneralRe: Not working on JBoss AS 6 PinmemberMember 102333239-Sep-13 3:13 
GeneralRe: Not working on JBoss AS 6 PinmemberOhad Redlich15-Sep-13 3:51 
GeneralRe: Not working on JBoss AS 6 PinmemberMember 1023332318-Sep-13 1:17 
Questionmailing list PinmemberMember 101473529-Jul-13 8:03 
AnswerRe: mailing list PinmemberOhad Redlich10-Jul-13 19:46 
GeneralMy vote of 5 PinmemberOhad Redlich6-Apr-13 8:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 3 Jul 2013
Article Copyright 2013 by Ohad Redlich
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid