使用java完成ldap身份验证

一:LDAP

LDAP是Lightweight Directory Access Protocol的缩写,即轻量级目录访问协议(这个主要是相对另一目录访问协议X.500而言的;LDAP略去了x.500中许多不太常用的功能,且以TCP/IP协议为基础)。目录服务和数据库很类似,但又有着很大的不同之处。数据库设计为方便读写,但目录服务专门进行了读优化的设计,因此不太适合于经常有写操作的数据存储。同时,LDAP只是一个协议,它没有涉及到如何存储这些信息,因此还需要一个后端数据库组件来实现。这些后端可以 是bdb(BerkeleyDB)、ldbm、shell和passwd等。

LDAP目录以树状的层次结构来存储数据(类同于DNS),最顶层即根部称作“基准DN”,形如"dc=domain,dc=com"或者"o= domain.com",前一种方式更为灵活也是Windows AD中使用的方式。在根目录的下面有很多的文件和目录,为了把这些大量的数据从逻辑上分开,LDAP像其它的目录服务协议一样使用OU (Organization Unit),可以用来表示公司内部机构,如部门等,也可以用来表示设备、人员等。同时OU还可以有子OU,用来表示更为细致的分类。

LDAP中每一条记录都有一个唯一的区别于其它记录的名字DN(Distinguished Name),其处在“叶子”位置的部分称作RDN;如dn:cn=tom,ou=animals,dc=domain,dc=com中tom即为 RDN;RDN在一个OU中必须是唯一的。 LDAP数据是“树”状的,这棵树是可以无限延伸的.

LDAP 其实就是一个数据库,但是跟我们平常的关系数据库有所不同。关系数据库是有一张一张的二维表格来存放数据的。ldap类似于dns系统,是树状的。用节点来存放数据。当然一个树枝可以有n个节点,每个节点上存放的数据,都是以key => value的形式。就像dns系统。 .是根,下面是com,org,net,cn等等一些树枝,这些树枝下面还有abc.com, bcd.com等等树枝。在每个树枝下面都可以放节点,其实就是域名下面的主机:www,ftp,mail等等。所有的这些内容,组成了一个dns树,在 ldap里面叫数据库。

存储LDAP配置信息及目录内容的标准文本文件格式是LDIF(LDAP Interchange Format),使用文本文件来格式来存储这些信息是为了方便读取和修改,这也是其它大多数服务配置文件所采取的格式。LDIF文件常用来向目录导入或更改记录信息,这些信息需要按照LDAP中schema的格式进行组织,并会接受schema 的检查,如果不符合其要求的格式将会出现报错信息。

在LDAP中目录是按照树型结构组织,目录由条目(Entry)组成,条目相当于关系数据库中表的记录;条目是具有区别名DN(Distinguished Name)的属性(Attribute)集合,DN相当于关系数据库表中的关键字(Primary Key);属性由类型(Type)和多个值(Values)组成,相当于关系数据库中的域(Field)由域名和数据类型组成, 只是为了方便检索的需要,LDAP中的Type可以有多个Value,而不是关系数据库中为降低数据的冗余性要求实现的各个域必须是不相关的。LDAP中条目的组织一般按照地理位置和组织关系进行组织,非常的直观。LDAP把数据存放在文件中,为提高效率可以使用基于索引的文件数据库,而不是关系数据库。LDAP协议集还规定了DN的命名方法、存取控制方法、搜索格式、复制方法、URL格式、开发接口等。LDAP对于这样存储这样的信息最为有用,也就是数据需要从不同的地点读取,但是不需要经常更新。常见的属性(Attribute)有:

二:在java中使用LDAP用于身份验证

要想在一个应用程序进入之前使用LDAP进行身份验证,在登录时后台接收要登录的用户名和密码,首先新建用于验证的类LDAPAuthentication

package com.hxkj.qrcode.action;

import java.util.Hashtable;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

public class LDAPAuthentication {
	private final String URL = "ldap://127.0.0.1:389/";
	private final String BASEDN = "ou=People,dc=example,dc=com";
	private final String FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
	private LdapContext ctx = null;
	private final Control[] connCtls = null;

	private void LDAP_connect() {
		Hashtable<String, String> env = new Hashtable<String, String>();
		env.put(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
		env.put(Context.PROVIDER_URL, URL + BASEDN);
		env.put(Context.SECURITY_AUTHENTICATION, "simple");
		
		String root = "cn=Directory Manager";// root
		env.put(Context.SECURITY_PRINCIPAL, root);
		env.put(Context.SECURITY_CREDENTIALS, "Admin123");
		// 此处若不指定用户名和密码,则自动转换为匿名登录
		try {
			ctx = new InitialLdapContext(env, connCtls);
		} catch (javax.naming.AuthenticationException e) {
			System.out.println("验证失败:" + e.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private String getUserDN(String uid) {
		String userDN = "";
		LDAP_connect();
		try {
			SearchControls constraints = new SearchControls();
			constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
			NamingEnumeration<SearchResult> en = ctx.search("", "uid=" + uid, constraints);
			if (en == null || !en.hasMoreElements()) {
				System.out.println("未找到该用户");
			}
			// maybe more than one element
			while (en != null && en.hasMoreElements()) {
				Object obj = en.nextElement();
				if (obj instanceof SearchResult) {
					SearchResult si = (SearchResult) obj;
					userDN += si.getName();
					userDN += "," + BASEDN;
				} else {
					System.out.println(obj);
				}
			}
		} catch (Exception e) {
			System.out.println("查找用户时产生异常。");
			e.printStackTrace();
		}

		return userDN;
	}

	public boolean authenricate(String UID, String password) {
		boolean valide = false;
		String userDN = getUserDN(UID);

		try {
			ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
			ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
			ctx.reconnect(connCtls);
			System.out.println(userDN + " 验证通过");
			valide = true;
		} catch (AuthenticationException e) {
			System.out.println(userDN + " 验证失败");
			System.out.println(e.toString());
			valide = false;
		} catch (NamingException e) {
			System.out.println(userDN + " 验证失败");
			valide = false;
		}

		return valide;
	}
}

在登录的action中,获取用户名(uid)、密码(password)

LDAPAuthentication _this = new LDAPAuthentication();

if(_this.authenricate(uid, password)){

  //进入主程序

  //主程序相关操作

}

如果验证通过,进入主程序。(验证时要开启LDAP服务,可以使用OpenDJ)。

OpenDJ是一个新的LDAPv3相容目录服务,为Java平台开发,提供了一个高性能的,高度可用和安全的企业管理的身份商店。其简单的安装过程中,结合了Java平台的力量,使OpenDJ简单和最快的目录服务器部署和管理。OpenDJ是Sun公司的发起OpenDS项目的延伸,并为它提供了一个完全支持的产品。基于开源和开放标准, OpenDJ(Open source Directory services for the Java platform) 是一个全新的 LDAPv3 兼容的目录服务,为企业提供了一个高性能,高可靠性的身份管理。它容易安装,加上Java平台优势,使得 OpenDJ 成为最简单和快速的目录服务器。
 
可以在OpenDJ中查看服务状态,管理相关条目。
可以在http://www.forgerock.org/opendj.html网站下载,下载完成后解压,打开setup.bat进行安装。
安装好以后,启动服务。也可以选择导入官方已有的配置文件ldif。
安装完成后,打开bat目录下的control-panel.bat进入OpenDJ控制面板,登陆后可以看到openDJ相关的基本信息和运行状态,可进行相关操作。
点击控制面板的“管理条目”,便可以打开创建时的基dn的管理界面。
这里需要密码,即安装时输入的密码。
 
三:tomcat+LDAP身份验证
目前正在研究中。具体配置可参见http://blog.csdn.net/davidgjm/article/details/2078826一文。

 

 

posted on 2013-05-29 09:18  liux_jiadukeji  阅读(12707)  评论(0编辑  收藏  举报