Click here to Skip to main content
15,867,939 members
Articles / Programming Languages / Java

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

Rate me:
Please Sign up or sign in to vote.
5.00/5 (6 votes)
3 Jul 2013CPOL2 min read 45.3K   456   8   16
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:

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.

C#
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

Java
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

Java
/**
* "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”.

Java
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)


Written By
Israel Israel
working for Intel

My Linkedin Profile

Visit my photography gallery

Comments and Discussions

 
QuestionWhere to get ado20-1.0.jar, active-directory-1.0.jar, and com4j-20110320.jar? Pin
opsaharan012-Feb-17 21:22
opsaharan012-Feb-17 21:22 
AnswerRe: Where to get ado20-1.0.jar, active-directory-1.0.jar, and com4j-20110320.jar? Pin
Ohad Redlich5-Nov-17 19:51
Ohad Redlich5-Nov-17 19:51 
QuestionNot working on JBoss AS 6 Pin
Member 1023332326-Aug-13 9:56
Member 1023332326-Aug-13 9:56 
AnswerRe: Not working on JBoss AS 6 Pin
Ohad Redlich5-Sep-13 8:17
Ohad Redlich5-Sep-13 8:17 
GeneralRe: Not working on JBoss AS 6 Pin
Member 102333236-Sep-13 1:27
Member 102333236-Sep-13 1:27 
GeneralRe: Not working on JBoss AS 6 Pin
Ohad Redlich6-Sep-13 3:08
Ohad Redlich6-Sep-13 3:08 
GeneralRe: Not working on JBoss AS 6 Pin
Member 102333236-Sep-13 3:35
Member 102333236-Sep-13 3:35 
Yes, we work with Maven. The JARs are downloaded from Maven Repo normally.

Actually, the first time we try to get data from LDAP, we get the following error:

java.lang.UnsatisfiedLinkError: Unable to load com4j.dll
	com4j.COM4J.loadNativeLibrary(COM4J.java:574)
	com4j.COM4J.<clinit>(COM4J.java:522)
	com.compal.rms.security.ActiveDirectoryUserInfo.initNamingContext(ActiveDirectoryUserInfo.java:29)
	com.compal.rms.security.ActiveDirectoryUserInfo.<init>(ActiveDirectoryUserInfo.java:60)
	com.compal.rms.security.ActiveDirectoryUserInfo.getInstanceNoCache(ActiveDirectoryUserInfo.java:47)
	com.compal.rms.security.ActiveDirectoryUserInfo.getInstance(ActiveDirectoryUserInfo.java:43)
	com.compal.rms.security.CustomAuthenticationFilter.doFilter(CustomAuthenticationFilter.java:130)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
	org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
	org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
	org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)



root cause 
java.lang.UnsatisfiedLinkError: no com4j-x86 in java.library.path
	java.lang.ClassLoader.loadLibrary(ClassLoader.java:1738)
	java.lang.Runtime.loadLibrary0(Runtime.java:823)
	java.lang.System.loadLibrary(System.java:1028)
	com4j.COM4J.loadNativeLibrary(COM4J.java:530)
	com4j.COM4J.<clinit>(COM4J.java:522)
	com.compal.rms.security.ActiveDirectoryUserInfo.initNamingContext(ActiveDirectoryUserInfo.java:29)
	com.compal.rms.security.ActiveDirectoryUserInfo.<init>(ActiveDirectoryUserInfo.java:60)
	com.compal.rms.security.ActiveDirectoryUserInfo.getInstanceNoCache(ActiveDirectoryUserInfo.java:47)
	com.compal.rms.security.ActiveDirectoryUserInfo.getInstance(ActiveDirectoryUserInfo.java:43)
	com.compal.rms.security.CustomAuthenticationFilter.doFilter(CustomAuthenticationFilter.java:130)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
	org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
	org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
	org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
	org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
	org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)



And then, every time we try again, we get the following error:

VB
java.lang.NoClassDefFoundError: Could not initialize class com4j.COM4J
    com.compal.rms.security.ActiveDirectoryUserInfo.initNamingContext(ActiveDirectoryUserInfo.java:29)
    com.compal.rms.security.ActiveDirectoryUserInfo.<init>(ActiveDirectoryUserInfo.java:60)
    com.compal.rms.security.ActiveDirectoryUserInfo.getInstanceNoCache(ActiveDirectoryUserInfo.java:47)
    com.compal.rms.security.ActiveDirectoryUserInfo.getInstance(ActiveDirectoryUserInfo.java:43)
    com.compal.rms.security.CustomAuthenticationFilter.doFilter(CustomAuthenticationFilter.java:130)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.session.ConcurrentSessionFilter.doFilter(ConcurrentSessionFilter.java:125)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:237)
    org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)

GeneralRe: Not working on JBoss AS 6 Pin
Ohad Redlich6-Sep-13 7:47
Ohad Redlich6-Sep-13 7:47 
GeneralRe: Not working on JBoss AS 6 Pin
Member 102333239-Sep-13 2:00
Member 102333239-Sep-13 2:00 
GeneralRe: Not working on JBoss AS 6 Pin
Ohad Redlich9-Sep-13 2:06
Ohad Redlich9-Sep-13 2:06 
GeneralRe: Not working on JBoss AS 6 Pin
Member 102333239-Sep-13 3:13
Member 102333239-Sep-13 3:13 
GeneralRe: Not working on JBoss AS 6 Pin
Ohad Redlich15-Sep-13 3:51
Ohad Redlich15-Sep-13 3:51 
GeneralRe: Not working on JBoss AS 6 Pin
Member 1023332318-Sep-13 1:17
Member 1023332318-Sep-13 1:17 
Questionmailing list Pin
Member 101473529-Jul-13 8:03
Member 101473529-Jul-13 8:03 
AnswerRe: mailing list Pin
Ohad Redlich10-Jul-13 19:46
Ohad Redlich10-Jul-13 19:46 
GeneralMy vote of 5 Pin
Ohad Redlich6-Apr-13 8:03
Ohad Redlich6-Apr-13 8:03 

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

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