(2-2009至6-2009)要做一套对项目开发、跟踪、管理、多服务器同步备份的系统集成。主要结合svn、apache、tomcat、bugzilla、sendmail、openSSL、LDAP这些开源优秀软件在ubuntu下实现。其中涉及到JAVA EE的WEB开发,EMAIL、NDS应用模块的配置和结合,数据加密,项目管理过程设计,SVN数据的备份与恢复等等。而我和几个teammates主要负责开发一个web应用程序,对svn中各个库的用户权限进行详细管理。
Linux下这些软件的结合是由一位Linux高手用了两个月时间,一步一步的配置起来,间中遇到的各种各样问题,在大家的努力下,终于把整个系统搭建起来。
因为公司原来就已经组建了一个非常完善的LDAP目录库,LDAP目录库,就像一个通信录,里面已经存放了所以公司人员的基本信息(如姓名,邮箱,职位等等)。这里有一个前提:所有的公司员工作为用户都可以登录这个web应用程序,登录后,系统则再根据那些SVN库对于这个用户是否有开放访问权限,如果有,则展现给用户。所以我们可以充分利用这个LDAP目录库,非常方便的管理这个WEB应用程序的使用用户。
现在不谈整个验证过程,就谈谈登录时,如何匹配LDAP目录库的信息,从而通过登录验证。匹配LDAP目录库记录时,要求要提供以下信息:LDAP目录库地址,基准DN,个人的CN,登录密码。如下面的一个例子:
LDAP目录库地址: ldap://10.67.10.2:3268/
基准DN: DC=corp,DC=sb
个人的CN: CN=Xiaopeng Deng,OU=HR,DC=CN,DC=corp,DC=sb
登录密码: 123456
sAMAccountName是个人的CN结点中的一个属性,例如上述的个人的CN的sAMAccountName的值为:xdeng。我命名它为shortname,即短名。在外国非常流行于使用shortname作为个人在公司中的称号,包括用于各种系统的登录。现在这个web应用程序也要使用这个sAMAccountName作为登录名登录。查了JAVA操作LDAP库的包,解决方法还是有的:
1,用户提供了sAMAccountName和密码,想登录系统。
2,首先要使用一个已知个人的CN及其密码,登录到LDAP目录库。登录成功后,这里会返回一个LDAP上下文类:InitialLdapContext。
3,利用这个上下文类中的方法:SearchControls,可以根据搜索条件字符串返回一个枚举类:NamingEnumeration,这时的搜索条件就可以指定sAMAccountName的值为用户输入的shortname。
4,如果搜索返回的枚举类中有值,则可以从这个对象中获取得它的 CN值。没有,则说明不存在这个用户
5,再根据这个CN值,和用户提供的密码,进行LDAP目录库的登录验证匹配过程。
目的其实非常单纯:先用一个已知的CN及其密码通过LDAP目录库验证,然后就可以查找到用户提供的shortname的对应CN是什么,最后就利用这个CN和用户提供的密码验证。最重要就是获得用户shortname的对应CN!
具体的类代码分享出来:(其中部分经过了我老大的修改后,更加完善,老大厉害!)
使用单态模式
代码中的bindDN,bindPassword变量值就是首先知道的CN及其密码,值放在资源文件中。
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.net.URL;
- import java.util.Hashtable;
- import java.util.Properties;
- import javax.naming.Context;
- import javax.naming.NameClassPair;
- import javax.naming.NamingEnumeration;
- import javax.naming.NamingException;
- import javax.naming.directory.DirContext;
- import javax.naming.directory.InitialDirContext;
- import javax.naming.directory.SearchControls;
- import javax.naming.ldap.Control;
- import javax.naming.ldap.InitialLdapContext;
- import javax.naming.ldap.LdapContext;
- import javax.naming.ldap.SortControl;
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import com.util.Constant;
- /**
- * LDAP Connector seems like JDBC, supposed to interface with AD.
- *
- * Use singleton pattern to prevent multi-instances.
- *
- */
- public class LDAPConnector {
- /** Logger for this class and subclasses */
- protected final Log log = LogFactory.getLog(getClass());
- private static LDAPConnector instance;
- private String url;
- private String baseDN;
- private String bindDN;
- private String bindPassword;
- private final Hashtable<String, String> env =new Hashtable<String, String>();
- private final Control[] sortConnCtls =new SortControl[1];
- {
- try {
- sortConnCtls[0] = new SortControl("sAMAccountName", Control.CRITICAL);
- } catch (IOException ex) {
- }
- }
- private LDAPConnector() {
- try {
- URL fileUrl = getClass().getClassLoader().getResource(Constant.FILE_LDAP_CONFIG);
- File resource = new File(fileUrl.getFile());
- Properties properties = new Properties();
- properties.load(new FileInputStream(resource));
- url = properties.getProperty("url");
- baseDN = properties.getProperty("baseDN");
- bindDN = properties.getProperty("bindDN");
- bindPassword = properties.getProperty("bindPassword");
- // set up environment for creating initial context
- env.put(Context.PROVIDER_URL, url + baseDN);
- env.put(Context.SECURITY_PRINCIPAL, bindDN);
- env.put(Context.SECURITY_CREDENTIALS, bindPassword);
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put("java.naming.batchsize","50");
- env.put("com.sun.jndi.ldap.connect.timeout","3000");
- env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
- env.put("com.sun.jndi.ldap.connect.pool","true");
- // the following pool parameters doesn't work
- // must setup as java init parameters
- env.put("com.sun.jndi.ldap.connect.pool.maxsize","3");
- env.put("com.sun.jndi.ldap.connect.pool.prefsize","1");
- env.put("com.sun.jndi.ldap.connect.pool.timeout","300000");
- env.put("com.sun.jndi.ldap.connect.pool.initsize","1");
- env.put("com.sun.jndi.ldap.connect.pool.authentication","simple");
- } catch (Exception e) {
- // ignore error
- e.printStackTrace();
- }
- }
- public static LDAPConnector getInstance() {
- if (instance == null)
- instance = new LDAPConnector();
- return instance;
- }
- public boolean validateUser(String username, String password) {
- boolean passed =false;
- LdapContext dirContext = null;
- try {
- // create initial context
- dirContext = new InitialLdapContext(env, sortConnCtls);
- dirContext.setRequestControls(sortConnCtls);
- SearchControls controls = new SearchControls();
- controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
- String filter = "(sAMAccountName=" + username +")";
- NamingEnumeration<?> answer = dirContext.search("", filter, controls);
- String userDN = null;
- while (answer.hasMore()) {
- userDN = ((NameClassPair) answer.nextElement()).getName();
- }
- // set up environment for creating initial context
- Hashtable<String, String> env = new Hashtable<String, String>();
- env.put(Context.PROVIDER_URL, url + baseDN);
- env.put(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN);
- env.put(Context.SECURITY_CREDENTIALS, password);
- env.put(Context.SECURITY_AUTHENTICATION, "simple");
- env.put("com.sun.jndi.ldap.connect.timeout","1000");
- env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
- // create initial context
- DirContext context = new InitialDirContext(env);
- passed = true;
- context.close();
- } catch (NamingException e) {
- // ignore error
- // e.printStackTrace();
- } finally {
- if (dirContext != null) {
- try {
- dirContext.close();
- } catch (NamingException e) {
- e.printStackTrace();
- }
- }
- }
- return passed;
- }
- }