Active Directory Authentication with Java on Linux

I have a simple task of authenticating Active Directory using Java. Just credential verification and nothing more. Say my domain is "fun.xyz.tld", the OU path is unknown, and the username / password is testu / testp.

I know there are several Java libraries that simplify this task, but I have not been able to implement them. Most of the examples I found addressed LDAP in general, not Active Directory. Issuing an LDAP request means sending an OU path to it that I don’t have. In addition, the application that issues the LDAP request must be bound to Active Directory in order to access it ... It is not safe, since the credentials must be stored in some place for discovery. I would like for test binding with test credentials, if possible, to mean that the account is valid.

Last, if possible, is there a way to make this authentication mechanism encrypted? I know that AD uses Kerberos, but I'm not sure if Java LDAP methods work.

Does anyone have an example of working code? Thank.

+62
java authentication active-directory ldap
Dec 23 '08 at 21:42
source share
9 answers

Here is the code I compiled using the example from this blog: LINK and this source: LINK .

import com.sun.jndi.ldap.LdapCtxFactory; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Iterator; import javax.naming.Context; import javax.naming.AuthenticationException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.DirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import static javax.naming.directory.SearchControls.SUBTREE_SCOPE; //import org.acegisecurity.AuthenticationException; import org.acegisecurity.BadCredentialsException; import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.providers.AuthenticationProvider; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider; import org.acegisecurity.userdetails.UserDetails; import org.acegisecurity.userdetails.UserDetailsService; import org.acegisecurity.userdetails.UsernameNotFoundException; class App2 { public static void main(String[] args) { if (args.length != 4 && args.length != 2) { System.out.println("Purpose: authenticate user against Active Directory and list group membership."); System.out.println("Usage: App2 <username> <password> <domain> <server>"); System.out.println("Short usage: App2 <username> <password>"); System.out.println("(short usage assumes 'xyz.tld' as domain and 'abc' as server)"); System.exit(1); } String domainName; String serverName; if (args.length == 4) { domainName = args[2]; serverName = args[3]; } else { domainName = "xyz.tld"; serverName = "abc"; } String username = args[0]; String password = args[1]; System.out .println("Authenticating " + username + "@" + domainName + " through " + serverName + "." + domainName); // bind by using the specified username/password Hashtable props = new Hashtable(); String principalName = username + "@" + domainName; props.put(Context.SECURITY_PRINCIPAL, principalName); props.put(Context.SECURITY_CREDENTIALS, password); DirContext context; try { context = LdapCtxFactory.getLdapCtxInstance("ldap://" + serverName + "." + domainName + '/', props); System.out.println("Authentication succeeded!"); // locate this user record SearchControls controls = new SearchControls(); controls.setSearchScope(SUBTREE_SCOPE); NamingEnumeration<SearchResult> renum = context.search(toDC(domainName), "(& (userPrincipalName=" + principalName + ")(objectClass=user))", controls); if (!renum.hasMore()) { System.out.println("Cannot locate user information for " + username); System.exit(1); } SearchResult result = renum.next(); List<GrantedAuthority> groups = new ArrayList<GrantedAuthority>(); Attribute memberOf = result.getAttributes().get("memberOf"); if (memberOf != null) {// null if this user belongs to no group at all for (int i = 0; i < memberOf.size(); i++) { Attributes atts = context.getAttributes(memberOf.get(i).toString(), new String[] { "CN" }); Attribute att = atts.get("CN"); groups.add(new GrantedAuthorityImpl(att.get().toString())); } } context.close(); System.out.println(); System.out.println("User belongs to: "); Iterator ig = groups.iterator(); while (ig.hasNext()) { System.out.println(" " + ig.next().toString()); } } catch (AuthenticationException a) { System.out.println("Authentication failed: " + a); System.exit(1); } catch (NamingException e) { System.out.println("Failed to bind to LDAP / get account information: " + e); System.exit(1); } } private static String toDC(String domainName) { StringBuilder buf = new StringBuilder(); for (String token : domainName.split("\\.")) { if (token.length() == 0) continue; // defensive check if (buf.length() > 0) buf.append(","); buf.append("DC=").append(token); } return buf.toString(); } } 
+44
Dec 26 '08 at 19:51
source share

There are three authentication protocols that can be used to authenticate between Java and Active Directory on Linux or on any other platform (and they are not only specific to HTTP services):

  • Kerberos - Kerberos provides Single Sign-On (SSO) and delegation, but web servers also need SPNEGO support to accept SSO through IE.

  • NTLM - NTLM supports SSO through IE (and other browsers, if configured correctly).

  • LDAP - LDAP binding can be used to easily verify the account name and password.

There is also something called “ADFS” that provides SSO for sites using SAML that invoke Windows SSP, so in practice it is basically a roundabout way of using one of the other protocols listed above.

Each protocol has its own advantages, but as a rule, for maximum compatibility, you usually try to "do what Windows does." So what does Windows do?

First, authentication between two Windows machines favors Kerberos because servers do not need to communicate with DCs and clients can cache Kerberos tickets, which reduces the load on DCs (and because Kerberos supports delegation).

But if the authenticating parties do not have domain accounts or if the client cannot contact the DC, NTLM is required. Thus, Kerberos and NTLM are not mutually exclusive, and NTLM does not expire Kerberos. In fact, in a way, NTLM is better than Kerberos. Note that when mentioning Kerberos and NTLM in one go, I should also mention SPENGO and Integrated Windows Authentication (IWA). IWA is a simple term that basically means Kerberos or NTLM or SPNEGO for Kerberos or NTLM negotiation.

Using LDAP binding as a way to verify credentials is inefficient and requires SSL. But until recently, the implementation of Kerberos and NTLM was difficult, so the use of LDAP as a shift-shift authentication service has been preserved. But at this point it should generally be avoided. LDAP is an information directory, not an authentication service. Use it as intended.

So, how do you implement Kerberos or NTLM in Java and in the context of web applications in particular?

There are many large companies, such as Quest Software and Centrify, which have solutions specifically mentioning Java. I can’t comment on them, because they are “identity management solutions” for the whole company, therefore, looking at the marketing on my website, it is difficult to say exactly which protocols are used and how. You will need to contact them for more information.

Implementing Kerberos in Java is not terribly difficult because the standard Java libraries support Kerberos through the org.ietf.gssapi classes. However, until recently, there was a serious obstacle - IE does not send raw Kerberos tokens, it sends SPNEGO tokens. But with Java 6, SPNEGO was implemented. Theoretically, you should write GSSAPI code that IE clients can authenticate. But I have not tried. The Sun Kerberos implementation has been a comedy of mistakes for many years, so based on Sun's record in this area, I would not have made any promises about their implementation of SPENGO until you have this bird.

For NTLM, there is a free OSS project called JCIFS that installs the NTLM HTTP authentication servlet filter. However, it uses a man-in-the-middle method to verify credentials with an SMB server that does not work with NTLMv2 (which is gradually becoming a necessary domain security policy). For this reason and others, part of the JCIFS HTTP filter is planned to be removed. Note that there are a number of side effects that use JCIFS to implement the same technique. Therefore, if you see other projects that claim to support SSO NTLM, check the small print.

The only correct way to verify NTLM credentials with Active Directory is to call NetRLogonSamLogon DCERPC through NETLOGON with the Secure Channel. Is there such a thing in Java? Yes. There he is:

http://www.ioplex.com/jespa.html

Jespa is a 100% Java NTLM implementation that supports NTLMv2, NTLMv1, full integrity and privacy settings, and the aforementioned NETLOGON authentication. And it includes an HTTP SSO Filter, a JAAS LoginModule, an HTTP client, a SASL client and server (with JNDI binding), a common "security provider" for creating custom NTLM services, etc.

Mike

+90
Jan 17 '09 at 18:14
source share

I just finished a project that uses AD and Java. We used Spring ldapTemplate.

AD is compatible with LDAP (almost), I don’t think you will have problems with your task. I mean the fact that this is AD or any other LDAP server, it does not matter if you just want to connect.

I would look: Spring LDAP

They also have examples.

As for encryption, we used an SSL connection (this was LDAPS). AD had to be configured on the SSL port / protocol.

But first of all, make sure that you are connecting to AD correctly through the LDAP IDE. I use Apache Directory Studio , it is really cool and it is written in Java. It's all I need. For testing purposes, you can also install Apache Directory Server

+6
Dec 23 '08 at 23:55
source share

Are you just checking your credentials? In this case, you can just make simple keberos and not worry about LDAP.

+3
Dec 23 '08 at 21:49
source share

As ioplex and others said, there are many options. For authentication using LDAP (and the Novell LDAP API), I used something like:

 LDAPConnection connection = new LDAPConnection( new LDAPJSSEStartTLSFactory() ); connection.connect(hostname, port); connection.startTLS(); connection.bind(LDAPConnection.LDAP_V3, username+"@"+domain, password.getBytes()); 

As a "special feature", Active Directory allows you to bind LDAP to "user @domain" without using a distinguished account name. This code uses StartTLS to enable TLS encryption in the connection; another alternative is LDAP over SSL, which is not supported by my AD servers.

The real trick is finding the server and host; the official way is to use DNS SRV lookup (service) to search for a packet of candidate nodes, and then ping LDAP based on UDP (in a specific Microsoft format) to find the correct server. If you're interested, I posted some blog articles about my journey of adventure and discovery in this area.

If you want to use Kerberos-based username and password authentication, you are looking at another fish maker; it is executable with Java GSS-API code, although I'm not sure if it performs the last step for authentication. (The code performing the verification can contact the AD server to verify the user name and password, which leads to the provision of a ticket for the user, but to ensure that the AD server does not impersonate the sample, it also needs to try to get a ticket for the user to itself which is a bit more complicated.)

If you want to do Kerberos-based single sign-on, if your users are authenticated in a domain, you can also do this with the Java GSS-API code. I would post a sample code, but I still need to turn my disgusting prototype into something suitable for the human eye. Check out some code from SpringSource for some inspiration.

If you are looking for NTLM (which made it clear to me, it is less secure) or something else, well, good luck.

+3
Nov 20 '09 at 19:56
source share

If all you want to do is authenticate against AD using Kerberos, then the simple http://spnego.sourceforge.net/HelloKDC.java program should do this.

Take a look at the pre-flight project documentation, which talks about HelloKDC.java.

+2
Nov 04 '09 at 16:10
source share
+1
Dec 23 '08 at 22:17
source share

ldap authentication without SSL is not secure, and everyone can view user credentials because the ldap client transfers the usernamae and password during the ldap binding operation. Therefore, always use the ldaps protocol. source: Ldap Active Directory Authentication in Java Spring Security with an example

+1
Nov 19 '11 at 16:35
source share

I recommend that you look at the adbroker package of the oVirt project. It uses Spring -Ldap and the JAAS Krb5 login module (with GSSAPI) to authenticate using Kerberos for Ldap servers (Active Directory, ipa, rhds, Tivoli-DS). Look for the code in engine \ backend \ manager \ modules \ bll \ src \ main \ java \ org \ ovirt \ engine \ core \ bll \ adbroker

You can use git to clone a repository or view using the gerrit link

0
Jun 18 2018-12-18T00:
source share



All Articles