I have a Spring MVC application (using 3.0.5 version), and need to bind to Active Directory using Spring LDAP for simply and only authenticating user's credentials. The application is hosted on a Linux server, so I need a cross-platform solution. And the application does not use Spring Security.
What is an effective way to implement user authentication in this setup? Active Directory supports FastBind
control (id= 1.2.840.113556.1.4.1781
), so I would like to leverage that since all I need is validation of the input credentials and need no other information back from AD.
Thanks!
Update (7/16/2012) : I will continue to update my question with details of the resolution.
Based on the answer from @ig0774 I wrote the following connection control
class:
package com.company.authentication;
import javax.naming.ldap.Control;
public class FastBindConnectionControl implements Control {
@Override
public String getID() {
return "1.2.840.113556.1.4.1781";
}
@Override
public boolean isCritical() {
return true;
}
@Override
public byte[] getEncodedValue() {
return null;
}
}
Then, I extended AbstractContextSource
, using the FastBind
connection-control class:
package com.company.authentication;
import java.util.Hashtable;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import org.springframework.ldap.core.support.AbstractContextSource;
public class FastBindActiveDirectoryContextSource extends AbstractContextSource {
@Override
protected DirContext getDirContextInstance(Hashtable env) throws NamingException {
return new InitialLdapContext(env, new Control[] { new FastBindConnectionControl() });
}
}
Finally, a service class to encapsulate the authentication mechanism:
package com.company.authentication;
import javax.naming.AuthenticationException;
import javax.naming.directory.DirContext;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.support.LdapUtils;
public class ActiveDirectoryAuthService implements IAuthenticate {
private ContextSource contextSource;
public void setContextSource(ContextSource contextSource) {
this.contextSource = contextSource;
}
@Override
public boolean authenticate(final String login, String password) {
try {
DirContext ctx = contextSource.getContext(login, password);
LdapUtils.closeContext(ctx);
return true;
}
catch (Exception e) {
return false;
}
}
}
In my Spring application context configuration file, I added the following:
<bean id="ADContextSource" class="com.company.authentication.FastBindActiveDirectoryContextSource">
<property name="url" value="ldaps://x.x.x.x:636" />
</bean>
<bean id="userAuthenticationService" class="com.company.authentication.ActiveDirectoryAuthService">
<property name="contextSource" ref="ADContextSource" />
</bean>
Finally, userAuthenticationService
bean is injected into the client class, say a login controller.
package com.company.web;
import com.company.authentication;
@Controller
public class LoginController {
@Autowired
private IAuthenticate userAuthenticationService;
public String authenticateUser(String login, String password) {
if (this.userAuthenticationService.authenticate(login, password)) {
return "welcome";
}
else {
return "login";
}
}
}
Implementing the FastBind
control in JNDI is pretty straight-forward as discussed in this OTN forum post .
Basically, you create a new Control
class for the FastBind
control:
class FastBindConnectionControl implements Control {
public byte[] getEncodedValue() {
return null;
}
public String getID() {
return "1.2.840.113556.1.4.1781";
}
public boolean isCritical() {
return true;
}
}
And then use that to create your ldap context (error handling and everything else ignored):
LdapContext ctx = new InitialLdapContext(env, new Control[] {new FastBindConnectionControl()});
Ideally, this would be easy to plug into Spring-LDAP which is a wrapper around the JNDI API for LDAP; however, it looks as though the interface for the built-in LdapContextSource
does not accept a parameter to deal with connection controls, so you'll apparently need to create your own sub-class of AbstractContextSource
to handle that, which looks like it should be straight-forward enough:
class FastBindLdapContextSource extends AbstractContextSource {
protected DirContext getDirContextInstance(Hashtable env) {
return new InitialLdapContext(env, new Control[] {new FastBindConnectionControl()});
}
}
You would then just need to replace your current LdapContextSource
with an instance of FastBindLdapContextSource
.
Note, however, that this context source can only be used for BIND
operations. As noted in the MSDN document I linked to in a comment to Terry Gardner:
Only simple binds are accepted on a connection in this mode. Because no group evaluation is done the connection is always handled as if no bind had occurred for the purposes of all other LDAP operations.
Which means that you're potentially looking at maintaining two types of LDAP contexts, one to do binds and one to actually perform any look-ups you may need to do.
Looking at the source code for LdapTemplate , I see the authenticate
method looks like it does a bit more than just a simple bind. More specifically, it does a search for the user and then attempts the bind. Since, if you're using a context with FastBind
enabled you're unlikely to be able to perform a search (usually AD doesn't permit searches on anonymous connections). Basically, that means you probably have to avoid LdapTemplate
.
However, assuming you get a reference to your ADContextSource bean, it should be simple enough to do something like
boolean authenticate(String username, String password) {
try {
DirContext ctx = contextSource.getContext(username, password);
LdapUtils.closeContext(ctx);
return true;
} catch (Exception e) {
// note: this means an exception was thrown by #getContext() above
return false;
}
}
Which fairly closely mimics what LdapTemplate
would do anyways (the only things that are missing is the AuthenticatedLdapEntryContextCallback
, which isn't of any value in this scenario, and the AuthenticationErrorCallback
, which could easily be added in if you want that behavior).
Since the FastBind
only "checks the user's credentials" and does not perform group determinations, it might be best to simply transmit a BIND request to the server using the distinguished name and credentials. Any simple BIND request should be transmitted over a secure connection. A response with a result code of 0
(zero) indicates that:
The UnboundID LDAP SDK is the best Java implementation for interacting with directory servers
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.