JNDI, Active Directory and Authentication -----SIMPLE
本文描述了使用Simple方式来进行验证。
很多开发者使用LDAP目录作为一个验证服务。但是LDAP作为一个目录协议,主要被设计用来搜索,添加,删除以及修改存储在目录中的实体,协议中暗含了通过使用一系列验证方法来验证LDAP连接。这些验证方法包括Simple(明文)、HTTP-DIGEST、X.509客户端证书。
当使用LDAP目录作为简单的验证服务时,典型的方法是获取用户的验证信息(用户名和密码),然后与存储在Active Directory中的用户名和密码相比较进行验证。
由于众所周知的安全原因,Active Directory不允许读取Windows密码(unicodePassword)属性,以此来防止攻击者获取密码或者以脱机方式破解密码。因此验证用户信息的唯一方式就是实现LDAP绑定。
通常情况下,当Active Directory验证一个用户的时候,它收集所有的验证数据和创建一个包含所有用户安全标志的Windows安全标记(组、成员、权限、…)。这适用于在Windows网络中验证用户,但是由可能导致额外的性能需求,不一定适用于许多企业内部或者外部的情况,在这些情况下只需要简单的校验用户名和密码。
Windows Server 2003介绍了一个LDAP连接控制来支持这种简单的情况,减少信息的收集。详细信息参考:
为了在Java&JNDI中使用Active Directory LDAP Fast Bind Control,只需要在连接请求时请求控制。在下面这个应用程序的服务器端代码中,LdapContext通过连接控制来进行初始化,后续的验证通过调用Context.reconnect方法。
package com.sun.auth.simple;
/**
* ldapfastbind.java
*
* Sample JNDI application to use Active Directory LDAP_SERVER_FAST_BIND connection control
*
*/
import java.util.Hashtable;
import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
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;
}
}
public class LdapFastBind {
public Hashtable env = null;
public LdapContext ctx = null;
public Control[] connCtls = null;
public LdapFastBind(String ldapurl) {
env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION,"simple");
env.put(Context.PROVIDER_URL,ldapurl);
connCtls = new Control[] {new FastBindConnectionControl()};
//first time we initialize the context, no credentials are supplied
//therefore it is an anonymous bind.
try {
ctx = new InitialLdapContext(env,connCtls);
}
catch (NamingException e) {
System.out.println("Naming exception " + e);
}
}
public boolean Authenticate(String username, String password) {
try {
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL,username);
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS,password);
ctx.reconnect(connCtls);
System.out.println(username + " is authenticated");
return true;
}
catch (AuthenticationException e) {
System.out.println(username + " is not authenticated");
return false;
}
catch (NamingException e) {
System.out.println(username + " is not authenticated");
return false;
}
}
public void finito() {
try {
ctx.close();
System.out.println("Context is closed");
}
catch (NamingException e) {
System.out.println("Context close failure " + e);
}
}
}
客户端代码只是简单的初始化SimpleSearch类,然后通过设置不同的用户来调用authenticate方法。
package com.sun.auth.simple;
/**
* fastbindclient.java
*
* Sample JNDI application to use LDAP_SERVER_FAST_BIND connection control
*
* This is just a test harness to invoke the ldapfastbind methods
*/
class FastBindClient {
public static void main(String[] args) {
// Could also use ldaps over port 636 to protect the communication to
// the
// Active Directory domain controller. Would also need to add
// env.put(Context.SECURITY_PROTOCOL,"ssl") to the "server" code
String ldapurl = "ldap://mydc.antipodes.com:389";
boolean IsAuthenticated = false;
LdapFastBind ctx = new LdapFastBind(ldapurl);
IsAuthenticated = ctx
.Authenticate("ANTIPODES//alberte", "GoodPassword");
IsAuthenticated = ctx.Authenticate("ANTIPODES//alberte", "BadPassword");
IsAuthenticated = ctx.Authenticate("ANTIPODES//charlesd",
"GoodPassword");
IsAuthenticated = ctx
.Authenticate("ANTIPODES//charlesd", "BadPassword");
IsAuthenticated = ctx.Authenticate("ANTIPODES//isaacn", "GoodPassword");
IsAuthenticated = ctx.Authenticate("ANTIPODES//isaacn", "BadPassword");
ctx.finito();
}
}