LDAP(輕量級目錄訪問協(xié)議,Lightweight Directory Access Protocol)是實現(xiàn)提供被稱為目錄服務(wù)的信息服務(wù)。目錄服務(wù)是一種特殊的數(shù)據(jù)庫系統(tǒng),其專門針對讀取,瀏覽和搜索操作進行了特定的優(yōu)化。目錄一般用來包含描述性的,基于屬性的信息并支持精細復(fù)雜的過濾能力。目錄一般不支持通用數(shù)據(jù)庫針對大量更新操作操作需要的復(fù)雜的事務(wù)管理或回卷策略。而目錄服務(wù)的更新則一般都非常簡單。這種目錄可以存儲包括個人信息、web鏈結(jié)、jpeg圖像等各種信息。為了訪問存儲在目錄中的信息,就需要使用運行在TCP/IP 之上的訪問協(xié)議—LDAP。
LDAP目錄中的信息是是按照樹型結(jié)構(gòu)組織,具體信息存儲在條目(entry)的數(shù)據(jù)結(jié)構(gòu)中。常見的例子是通訊簿,由以字母順序排列的名字、地址和電話號碼組成。
目錄服務(wù)與關(guān)系數(shù)據(jù)庫之間的主要區(qū)別在于:二者都允許對存儲數(shù)據(jù)進行訪問,只是目錄主要用于讀取,其查詢的效率很高,而關(guān)系數(shù)據(jù)庫則是為讀寫而設(shè)計的。也就是目錄服務(wù)不適于進行頻繁的更新,屬于典型的分布式結(jié)構(gòu)。
總結(jié):對于查詢操作多于更新操作的(認(rèn)證)系統(tǒng)來說,使用OpenLDAP是一個比關(guān)系數(shù)據(jù)庫如MySq、PostgreSQL等更好的選擇。
在LDAP的功能模型中定義了一系列利用LDAP協(xié)議的操作,主要包含以下4部分:
查詢操作:允許查詢目錄和取得數(shù)據(jù),其查詢性能比關(guān)系數(shù)據(jù)庫好。
更新操作:目錄的更新操作沒關(guān)系數(shù)據(jù)庫方便,更新性能較差,但也同樣允許進行添加、刪除、修改等操作。
復(fù)制操作:前面也提到過,LDAP是一種典型的分布式結(jié)構(gòu),提供復(fù)制操作,可將主服務(wù)器的數(shù)據(jù)的更新復(fù)制到設(shè)置的從服務(wù)器中。
認(rèn)證和管理操作:允許客戶端在目錄中識別自己,并且能夠控制一個會話的性質(zhì)。
而本文所要將的OpenLDAP就是一個優(yōu)秀的開源的LDAP實現(xiàn)。
安裝軟件非常簡單,但在配置過程中遇到了不少坎坷,不是服務(wù)啟動不成功就是驗證不成功。
具體的安裝和配置方法網(wǎng)上一大把,但都參差不齊,主要是因為新舊版本的OpenLDAP不同,配置方法有很大的改動。
下面給出網(wǎng)上幾個還算靠譜的Linux和Windows兩個平臺下安裝該軟件的方法:
1)ubuntu安裝LDAP:安裝方法靠譜,但配置說的不太清楚,配置注意事項看后面。
2)Ubuntu OpenLDAP Server:官方教程,最值得借鑒,是英文的,這里有中文版的,但沒英文的清晰,說的比較簡單。
3)Linux下安裝openldap:二進制包安裝方法,適用于非Ubuntu的Linux系統(tǒng),稍微有點麻煩,在安裝OpenlDAP之前還需要安裝Berkeley DB,但配置靈活,可以自定義安裝路徑什么的。后面的配置也沒說清楚,主要看安裝方法。
4)Linux服務(wù)器部署系列之七—OpenLDAP篇:另一篇較詳細的二進制安裝方法及配置。
4)Windows下OpenLDAP的安裝及使用:介紹了LDAP的一些基礎(chǔ)知識和Windows下安裝方法。
5)圖文介紹openLDAP在windows上的安裝配置:比較詳細,值得一看。
上面給出的這幾個鏈接雖然還不錯,但還是欠缺了些什么?對,就是講解,網(wǎng)上給出的教程都是手把手教你如何安裝和配置,而沒有說明版本差異、具體配置的含義及為什么這樣配置,如果因為版本或環(huán)境差異,你按其方法配置不成功,你也不知道哪里出的問題,因此建議還是先熟悉LDAP的基礎(chǔ)知識,配置文件含義然后再試著安裝。
下面根據(jù)我自己的經(jīng)驗,給出幾個安裝和配置注意事項,供參考。
疑惑1:細心的人會發(fā)現(xiàn)有的教程說要配置主機DNS,添加與LDAP相關(guān)的域名,而大部分教程都沒有提及這個,那么到底要不要配置呢?
解答:當(dāng)然需要配置。安裝好OpenLDAP后首先需要配置slapd.conf這個文件,其中里面有
suffix "dc=example, dc=com"
這樣一句需要自己配置,這兩個dc代表什么意思呢?其實dc就是“domainComponent”,也就是域名的組成部分,準(zhǔn)確的說是主機域名的 后綴組成部分,如果這里的配置與你的主機域名不對應(yīng)的話,服務(wù)一般是啟動不了的。那么怎么配置域名呢?Linux和Windows下的配置文件如下:
Linux下:/etc/hosts
Windows下:C:\Windows\System32\drivers\etc\hosts
需要在hosts文件里添加一條域名(如果沒配置的話),格式如下:
127.0.1.1 hostname.example.com hostname
比如我的主機名是min,并添加的域名配置是:
127.0.1.1 min.alexia.cn min
那么相應(yīng)的我就需要在slapd.conf里這樣配置suffix:
suffix "dc=alexia, dc=cn"
當(dāng)然這里域名后綴不一定只有兩級,也可以是hostname.example.com.cn,然后suffix就應(yīng)該是“dc=example, dc=com, dc=cn”,這隨便你怎么設(shè)置了,只要對應(yīng)就行。
疑惑2:很多版本的slapd.conf里默認(rèn)都配置了下面兩個變量:
modulepath /usr/lib/ldapmoduleload back_@BACKEND@
這是什么意思?需要改動嗎?
解答:這是數(shù)據(jù)庫database的backend,一般slapd.conf里配置的database都是 bdb,也就是Berkeley DB,有的也許是hdb,其實也是Berkeley DB,只是兩個不同的存儲引擎(就像Mysql有MyISAM和InnoDB兩個不同的存儲引擎一樣)。而modulepath和moduleload指 定了動態(tài)模塊路徑及動態(tài)裝載的后端模塊,因為OpenLDAP默認(rèn)是用Berkeley DB存儲數(shù)據(jù)的,如果你有動態(tài)的數(shù)據(jù)需要裝載,那么就需要配置這兩個參數(shù),對于一般用戶將這兩個注釋掉即可。
疑惑3:OpenLDAP默認(rèn)采用Berkeley DB存儲數(shù)據(jù),那么可以換用其它的關(guān)系數(shù)據(jù)庫嗎?具體如何配置呢?
解答:當(dāng)然可以。首先需要明確ldap數(shù)據(jù)模型來自RDBMS(關(guān)系數(shù)據(jù)庫模型),而并沒有指定一定是哪個DB,只要是關(guān)系數(shù)據(jù)庫都可以作為LDAP的后臺,那么你為什么會想用其它的數(shù)據(jù)庫代替自帶的Berkeley DB呢?我想可能是性能相關(guān)了,對于少量數(shù)據(jù)你用哪個都可以,但若涉及到稍大點的數(shù)據(jù),比如成千上萬的用戶查詢,那么Berkeley DB的性能就不可觀了,而且Berkeley DB管理起來也不太方便,畢竟對這個數(shù)據(jù)庫熟悉的人不多,如果能換作我們經(jīng)常使用的數(shù)據(jù)庫,不僅性能得到提升,管理起來也十分容易,豈不是一舉多得。
具體怎么配置了,請參考這篇文章:用postgresql作后臺的openldap,以PostgreSQL作為例子進行講解。
疑惑4:新舊版本的OpenLDAP到底有什么差異呢?
解答:簡單一句話就是:舊版本的OpenLDAP配置文件一般是slapd.conf(路徑可能是/etc/openldap,也可能是/usr/local/openldap,甚至可能是/usr/share/slapd/,不同版本不同安裝不同系統(tǒng)都可能不同,可使用locate slapd.conf進行查找正確的路徑),而新版本(我測試的新版本是2.4.31)的OpenLDAP服務(wù)運行時并不會讀取該配置文件,而是從slapd.d目錄(一般與slapd.conf在同一目錄下)中讀取相關(guān)信息,我們需要把該目錄下的數(shù)據(jù)刪掉,然后利用我們在slapd.conf里配置的信息重新生成配置數(shù)據(jù)。這也可能是你啟動服務(wù)后運行l(wèi)dap相關(guān)命令卻出現(xiàn)“ldap_bind: Invalid credentials (49)”錯誤的主要原因。具體怎么重新生成配置數(shù)據(jù)請看參考資料。
疑惑5:自定義的ldif數(shù)據(jù)文件中的objectclass后的domain、top、organizationalUnit、inetOrgPerson等等都是什么意思,可以隨便寫嗎?
解答:存儲LDAP配置信息及目錄內(nèi)容的標(biāo)準(zhǔn)文本文件格式是LDIF(LDAP Interchange Format),使用文本文件來格式來存儲這些信息是為了方便讀取和修改,這也是其它大多數(shù)服務(wù)配置文件所采取的格式。LDIF文件常用來向目錄導(dǎo)入或更改記錄信息,這些信息需要按照LDAP中schema的格式進行組織,并會接受schema 的檢查,如果不符合其要求的格式將會出現(xiàn)報錯信息。因此,ldif文件中的屬性都定義在各大schema中,其中objectclass是對象的類屬性,不能隨便填寫,而應(yīng)與schema中一致。一般slapd.conf文件的頭部都包含了這些schema:
include ../etc/openldap/schema/core.schemainclude ../etc/openldap/schema/cosine.schemainclude ../etc/openldap/schema/inetorgperson.schemainclude ../etc/openldap/schema/nis.schemainclude ../etc/openldap/schema/krb5-kdc.schemainclude ../etc/openldap/schema/RADIUS-LDAPv3.schemainclude ../etc/openldap/schema/samba.schema
其中前三個是比較重要的schema,定義了我們所需要的各個類,比如ldif中一般先定義一個根節(jié)點,其相應(yīng)的objectclass一般是 domain和top,而根節(jié)點下的ou屬性即定義組節(jié)點(group)的objectclass一般是 organizationalUnit,group下可以是group也可以是用戶節(jié)點,用戶節(jié)點的objectclass一般是 inetOrgPerson。而各個節(jié)點的一系列屬性如用戶節(jié)點的uid、mail、userPassword、sn等等都定義在schema中相關(guān)的 objectclass里,可以自己查找看看。
疑惑6:OpenLDAP認(rèn)證用戶uid時默認(rèn)是不區(qū)分大小寫的,也就是“alexia”與“AleXia”是同一個用戶,在有些情況下這并不合理,能配置使得認(rèn)證時能區(qū)分大小寫嗎?
解答:以我目前的經(jīng)驗來看,舊版本的OpenLDAP是可以配置區(qū)分大小寫的,而新版本的OpenLDAP卻配置不了。為什么這么說呢?
這里就涉及到“matching rules”這個概念了,即匹配規(guī)則,就是各個屬性按什么樣的規(guī)則進行匹配,比如是否區(qū)分大小寫、是否進行數(shù)字匹配等等,這里有詳細的官方匹配規(guī)則描述。比如舊版本的core.schema里有下面這樣一段:
attributetype ( 0.9.2342.19200300.100.1.1 NAME ( 'uid' 'userid' ) DESC 'RFC1274: user identifier' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
從字面上也可以看出,其中caseIgnoreMatch
和caseIgnoreSubstringsMatch
就定義了uid或userid屬性匹配時不區(qū)分大小寫,如果我們將其改為caseExactMatch
和caseExactSubstringsMatch
就表示用戶uid認(rèn)證時需要區(qū)分大小寫,也就是“alexia”與“AleXia”同不同的用戶,這很簡單,在舊版本的OpenLDAP也行得通。
可是在新版本的OpenLDAP中卻不行,新版本的core.schema文件中也包含這樣一段:
#attributetype ( 2.16.840.1.113730.3.1.217# NAME ( 'uid' 'userid' )# DESC 'RFC1274: user identifier'# EQUALITY caseIgnoreMatch# SUBSTR caseIgnoreSubstringsMatch# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
可惜是注釋掉的,那我們?nèi)∠⑨屓缓蟾膶傩孕胁恍心??答案是不行,會報錯:Duplicate attributeType: "2.16.840.1.113730.3.1.217”,也就是說該屬性已經(jīng)被定義了,然后我就去包含的所有schema中搜索uid屬性的定義,結(jié)果卻找不到定義,那么為什么還會報這個錯誤呢?后來一陣搜索,終于在這個帖子“slapd: built-in schema for uidNumber/gidNumber does not have ordering directive”知道了答案,原來新版本的OpenLDAP已經(jīng)把uid屬性定義schema硬編碼到了slapd程序中,也就是無法在配置文件中修改了,真是坑!
針對這個問題,我給出兩個不太好的解決方案:
我的主要經(jīng)驗也就這些。OpenLDAP也有客戶端,如果你配置成功后,可以用客戶端或?qū)慗ava程序進行驗證。
OpenLDAP既有圖形客戶端也有網(wǎng)頁客戶端。
主要有兩個圖形客戶端:LdapBrowser282 (下載:LdapBrowser282.zip,下載解壓后直接雙擊:lbe.bat 文件即可運行)和LdapAdmin(官方下載),使用都非常簡單。
如下是兩個客戶端的界面,都需要先建立一個鏈接,填上相應(yīng)的IP地址、端口和dn配置,然后連接即可獲得你配置的數(shù)據(jù)。
LDAP Admin客戶端:
即 phpLDAPadmin,基于PHP的一個web應(yīng)用,需要配置Apache服務(wù)器和PHP,具體的配置方法可參考“phpLDAPadmin 安裝配置講解,通過 Web 端來管理您的 LDAP 服務(wù)器”,我比較偷懶,直接使用的PHPnow全套服務(wù),安裝成功后大概是下面這樣一個界面:
下面借鑒網(wǎng)上資料提供一個簡單的認(rèn)證程序如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | 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 BASEDN = "ou=Tester,dc=alexia,dc=cn" ; // 根據(jù)自己情況進行修改 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=manager,dc=alexia,dc=cn" ; // 根,根據(jù)自己情況修改 env.put(Context.SECURITY_PRINCIPAL, root); // 管理員 env.put(Context.SECURITY_CREDENTIALS, "123456" ); // 管理員密碼 try { ctx = new InitialLdapContext(env, connCtls); System.out.println( "認(rèn)證成功" ); } catch (javax.naming.AuthenticationException e) { System.out.println( "認(rèn)證失敗:" ); e.printStackTrace(); } catch (Exception e) { System.out.println( "認(rèn)證出錯:" ); e.printStackTrace(); } if (ctx != null ) { try { ctx.close(); } catch (NamingException 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( "查找用戶時產(chǎn)生異常。" ); 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; } public static void main(String[] args) { LDAPAuthentication ldap = new LDAPAuthentication(); if (ldap.authenricate( "gygtest" , "jmwang" ) == true ){ System.out.println( "該用戶認(rèn)證成功" ); } } } |
既可以作為普通程序的認(rèn)證,也可以通過輸出檢查自己的配置是否正確。
LDAP的實現(xiàn)除了OpenLDAP外,還有其它,比如OpenDJ(Open source Directory services for the Java platform),它是一個新的LDAPv3相容目錄服務(wù),為Java平臺開發(fā),提供了一個高性能的,高度可用和安全的企業(yè)管理的身份商店。其簡單的安裝過程中,結(jié)合了Java平臺的力量,使OpenDJ簡單和最快的目錄服務(wù)器部署和管理。有興趣的可以查閱相關(guān)資料。