Lotus的JAVA與DOMINO通過LDAP整合方法!
JAVA, DOMINO, Lotus, LDAP
摘要:LDAP是標準輕量目錄訪問協議(Lightweight Directory Access Protocol),通過LDAP,你可以訪問目錄中的使用者資訊,進行使用者驗證。DominoR5/6支援標準的LDAP v3目錄服務。本文通過描述使用JNDI訪問Domino目錄的過程,為大家介紹如何充分利用機構中已有的Domino目錄資源。本文所述的方法也適用於其他支援LDAP v3的目錄伺服器。
為什麼目錄服務如此重要
隨著企業中應用程式的增加,我們不得不面對日益增長的各類使用者資料。這些使用者資料分布在企業各處,帶來了很大管理和維護上的麻煩。為瞭解決這個問題,我們通常需要在企業中構建標準的目錄基礎設施。同時,在實施EAI(公司專屬應用程式整合)時,我們還經常會遇到使用者需要單點登入(SSO)的情況,而成功實施SSO的基礎是我們有一個好的目錄基礎。
目前,在很多的機構中都實施了基於Domino的應用,例如:辦公、郵件、審批等。做為一個相容各種標準的優秀平台,Domino也提供了對LDAP v3的良好支援。
那麼,對於已經架設Domino基礎設施的機構,是否考慮從現有的投資中獲得更多的回報呢。下面,我們通過兩個範例來瞭解如何充分利用這些儲存在Domino中的目錄資源。
準備工作
1、 瞭解JNDI
JNDI(Java Naming And Directory Interface)是在Java中訪問各種命名和目錄服務的規範。它通過一組擴充的API:javax.naming.*來提供對命名和目錄服務的訪問。
使用JNDI前,你必須確保你擁有jndi.jar,並且在當前ClassPath中包含它。如果你還沒有jndi.jar,可通過參考資料處下載。
2、 配置Domino伺服器
在使用下列範例前,你必須先啟動你的Domino伺服器(我使用R6版本)中,並且啟用LDAP服務。
由於我們不需要使用任何特殊的選項,我們無須對Domino的LDAP配置做出任何修改,僅須確認已載入了LDAP服務(在Domino Console上輸入:show tasks來查看是否載入了LDAP服務)。如果還沒有載入LDAP服務,你可以通過在Domino的伺服器控制台上輸入:load ldap來載入它。
讀取目錄資訊
下面的代碼從目錄中讀取給定使用者的郵件地址。
package net.eservice4you.ldap;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.DirContext;
import javax.naming.directory.Attributes;
import javax.naming.NamingException;
import java.util.Hashtable;
class Getattr {
public static void main(String[] args) {
// Identify service provider to use
//將初始化的資訊放到一個hashtable中去
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=BJCHP");
// 你需要將localhost改為domino伺服器的全稱或IP,並且,將O=改為你的Domino組織名稱
try {
// Create the initial directory context
DirContext ctx = new InitialDirContext(env);
// Ask for all attributes of the object
// 你需要將xinxibu改為服務上已有的註冊使用者
Attributes attrs = ctx.getAttributes("cn=xinxibu");
// Find the mail address and print it
System.out.println("Mail: " + attrs.get("mail").get());
// Close the context when we're done
ctx.close();
} catch (NamingException e) {
System.err.println("Problem getting attribute: " + e);
}
}
}
從代碼中,我們看到讀取目錄資訊的過程:
1. 設定所使用的Service Provider 和Service URI到Hashtable中。
2. 初始化一個DirContext。
3. 使用Context.getAttributes來擷取指定使用者的所有屬性資訊。
4. 使用attrs.get(“xxx”)來獲得屬性資訊。
5. 關閉Context的串連。
註:在Domino LDAP目錄預設只允許尋找部分使用者資訊。你可以查看Domino Administrator Help中“Domino目錄/Domino LDAP服務”一節,加入更多的屬性到列表中。
驗證使用者身份
下面的代碼通過請求LDAP使用者登入,來驗證使用者的身份。
package net.eservice4you.ldap;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.DirContext;
import javax.naming.NamingException;
import javax.naming.AuthenticationException;
import java.util.Hashtable;
class AuthUser {
public static void main(String[] args) {
// Identify service provider to use
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=BJCHP");
// 你需要將localhost改為domino伺服器的全稱或IP,並且,將O=改為你的Domino組織名稱
// Authenticate as xinxibu and password "1234"
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, "cn=xinxibu,o=BJCHP");
env.put(Context.SECURITY_CREDENTIALS, "1234");
try {
// Create the initial directory context
DirContext ctx = new InitialDirContext(env);
System.out.println("Authentication Ok!");
ctx.close();
}catch (AuthenticationException e){
// 如果捕捉到AuthenticationException,表示驗證失敗
System.err.println("Authentication Fail:"+e);
} catch (NamingException e) {
System.err.println("Nameing Exception: " + e);
}
}
}
我們可以看到,要驗證使用者,我們需要做以下幾件事:
1. 設定所使用的Service Provider 和URL到Hashtable中。
2. 設定驗證方式,使用者名稱和密碼(對於simple)。
3. 初始化一個DirContext。如果捕捉到AuthenticationException異常,表示驗證失敗。
4. 關閉Context的串連。
除了我們剛才使用的simple方式外,還可以使用Digest-MD5, Kerberos V5和X.509等方式來驗證。你可以查看參考資料來瞭解具體的細節。
總結
如果你的機構中已使用了Domino,如果你需要應用整合,是時候考慮LDAP。
本文所述的方法,同樣適用於IBM Directory Server和OpenLDAP等其他目錄伺服器。我在JDK 1.4.1+ Domino 6下測試通過了這些代碼。你也可以使用Domino 5,它同樣可以工作的很好。
參考資料
** 下載範例代碼:jndiExample.jar
** 訪問java.sun.com/products/jndi可以瞭解有關JNDI的最新情況,下載JNDI API。你還看看JNDI Tutorial : java.sun.com/products/jndi/tutorial/index.html。
** 關於LDAP,你可以訪問 LDAPman RFC 頁面來瞭解LDAP RFC。在developer works上,你還可以找到一篇關於如何構建LDAP地址本的教學:http://www-900.ibm.com/developer ... tutorial/index.html。
JNDI(Java Naming and Directory Interface,Java命名和目錄介面)是為了Java程式訪問命名服務和目錄服務而提供的統一API。
命名服務,說白了就是提供一個名稱索引值對的管理,即Key-Value對,Key代表一個資源的名稱,Value代表資源的真真實位址,命名服務允許大家通過唯一的名稱找到對應的對象或資源。這樣程式只需要知道某種資源的名稱,就可以通過JNDI來訪問到它,而不需要知道這個資源真實的物理地址。這有點類似於DNS服務,DNS服務將網域名稱解析成IP地址,這樣大家只需要在瀏覽器中輸入網站的唯一名稱(即網域名稱)就可以訪問到該網站,而不需要記住這個網站真實的IP地址。
目錄服務,提供的也是一種公用資源的管理服務。目錄服務是一種特殊類型的資料庫,它按照一定的資料結構,比如樹型結構,把各種公用資源群組織並儲存起來。這種特殊資料庫與傳統關係型資料庫的區別在於,它對查詢作了最佳化,其資料結構允許大家非常快速的找到想要的資源,即保障了一種快速尋找能力,不過這種設計也犧牲了其他方面的效率,比如它的更新效率就要低得多。
目錄服務中管理的也是名稱索引值對,不過其索引值是具有階層的,像一棵樹,即通過一個名稱或一個帶階層的名稱,你可以定位到一顆子樹,而不只是一個屬性。由此可見,目錄服務將命名服務的概念進一步引申為提供具有階層的資訊庫。一個目錄服務通常擁有一個命名服務,但是一個命名服務不必具有一個目錄服務。
目前,有許多不同廠商提供目錄服務產品如NIS、NDS、Active Directory、或者完全的LDAP目錄服務,類似於不同資料庫廠商的資料庫產品,這些目錄服務產品的實現是不一樣的,各有各的特點。但他們的前端訪問都支援LDAP協議。
LDAP(lightweight directory access protocol,輕量級目錄訪問協議)是在20世紀90年代早期作為標準目錄協議進行開發的。它是目前最流行的目錄協議,與廠商、具體平台無關。LDAP用統一的方式定義了如何訪問目錄服務中的內容,比如增加、修改、刪除一個條目。每個具體的目錄服務廠商都會向外界提供LDAP協議訪問本產品的介面,這樣,我們只需要統一關心如何使用LDAP協議就可以了。
JNDI則是Java中用於訪問LDAP的API,開發人員使用JNDI完成與LDAP伺服器之間的通訊,即用JNDI來訪問LDAP,而不需要和具體的目錄服務產品特性打交道。這樣通過LDAP、JNDI兩層抽象,使Java程式對目錄服務的訪問做到了平台無關性。
JNDI API是J2SE中的標準API,是通用的API。JNDI中包括5個包:javax.naming,javax.naming.directory,javax.naming.event,javax.naming.ldap,javax.naming.spi。
常用的JNDI操作有:bind,unbind,lookup,rename,NamingEnumeration listBindings,NamingEnumeration list。
使用JNDI來訪問命名服務或目錄服務,操作步驟如下:
1、建立一個HashTable,包含定義所希望使用的JNDI服務的屬性,所希望串連的LDAP伺服器IP地址以及連接埠;
2、將與認證成使用者登入有關的任何資訊添加到散列表中;
3、建立初始context對象。如果訪問命名服務,則使用InitialContext類,如果訪問目錄服務,則要使用InitialDirContext類;
4、使用剛才得到的context對象執行所需的操作,如添加新的條目或者搜尋條目;
5、完成操作後關閉context對象。
以下代碼,實測可用:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;
public class findUseBindObj {
public static void main(String[]args){
//建立Hashtable以儲存JNDI將用於串連目錄服務的環境變數
Hashtable hs = new Hashtable();
//設定串連Ldap的實現工廠
hs.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
// 指定LDAP伺服器IP地址為本機及連接埠號碼為389
hs.put(Context.PROVIDER_URL, "ldap://testoa.dgoa.cn:389");
try {
// 得到初始目錄環境的一個引用
DirContext ctx = new InitialDirContext(hs);
//利用lookup尋找返回指定DN的條目對象
//persons pers =(persons)ctx.lookup("cn=輝,o=東莞市");
// 利用遠程對象調用遠程方法,返回Age變數的值
//String age = pers.getAge();
// 利用遠程對象調用遠程方法,返回Name變數的值
//String name = pers.getName();
//輸出Name的值
//System.out.println("name is :" + name );
/*根據結點的DN來尋找它的所有屬性, 然後再從屬性中得到所有的值,注意一個屬性可
以有多個值*/
Attributes attrs=ctx.getAttributes("cn=輝,o=東莞市");
//迴圈擷取並輸出這個屬性的所有屬性值
for(NamingEnumeration ae = attrs.getAll();ae.hasMore();){
//擷取一個屬性
Attribute attr = (Attribute)ae.next();
System.out.println("Attribute : " + attr.getID());
//迴圈取得輸出這個屬性的所有屬性值
for(NamingEnumeration ve = attr.getAll();ve.hasMore();){
System.out.println(" Value : " + ve.next());
}
}
//成功列印提示資訊
System.out.println("find object success " );
//調用該對象的函數
//pers.toString();
//關閉初始目錄環境
ctx.close();
} catch (NamingException ex) {
System.err.println(ex.toString());
}
}