OpenLDAP使用疑惑解答及使用Java完成LDAP身份認證

導讀

LDAP(輕量級目錄訪問協議,Lightweight Directory Access Protocol)是實現提供被稱爲目錄服務的信息服務。目錄服務是一種特殊的數據庫系統,其專門針對讀取,瀏覽和搜索操作進行了特定的優化。目錄一般用來包含描述性的,基於屬性的信息並支持精細複雜的過濾能力。目錄一般不支持通用數據庫針對大量更新操作操作需要的複雜的事務管理或回捲策略。而目錄服務的更新則一般都非常簡單。這種目錄可以存儲包括個人信息、web鏈結、jpeg圖像等各種信息。爲了訪問存儲在目錄中的信息,就需要使用運行在TCP/IP
之上的訪問協議—LDAP。
LDAP目錄中的信息是是按照樹型結構組織,具體信息存儲在條目(entry)的數據結構中。常見的例子是通訊簿,由以字母順序排列的名字、地址和電話號碼組成。
目錄服務與關係數據庫之間的主要區別在於:二者都允許對存儲數據進行訪問,只是目錄主要用於讀取,其查詢的效率很高,而關係數據庫則是爲讀寫而設計的。也就是目錄服務不適於進行頻繁的更新,屬於典型的分佈式結構。
總結:對於查詢操作多於更新操作的(認證)系統來說,使用OpenLDAP是一個比關係數據庫如MySq、PostgreSQL等更好的選擇。

LDAP的功能

在LDAP的功能模型中定義了一系列利用LDAP協議的操作,主要包含以下4部分:
查詢操作:允許查詢目錄和取得數據,其查詢性能比關係數據庫好。
更新操作:目錄的更新操作沒關係數據庫方便,更新性能較差,但也同樣允許進行添加、刪除、修改等操作。
複製操作:前面也提到過,LDAP是一種典型的分佈式結構,提供複製操作,可將主服務器的數據的更新複製到設置的從服務器中。
認證和管理操作:允許客戶端在目錄中識別自己,並且能夠控制一個會話的性質。

而本文所要將的OpenLDAP就是一個優秀的開源的LDAP實現。

OpenLDAP安裝配置及疑惑解答

  1. 安裝和配置OpenLDAP
    安裝軟件非常簡單,但在配置過程中遇到了不少坎坷,不是服務啓動不成功就是驗證不成功。
    具體的安裝和配置方法網上一大把,但都參差不齊,主要是因爲新舊版本的OpenLDAP不同,配置方法有很大的改動。
    下面給出網上幾個還算靠譜的Linux和Windows兩個平臺下安裝該軟件的方法:
    1)ubuntu安裝LDAP:安裝方法靠譜,但配置說的不太清楚,配置注意事項看後面。
    2)Ubuntu OpenLDAP Server:官方教程,最值得借鑑,是英文的,這裏有中文版的,但沒英文的清晰,說的比較簡單。
    3)Linux下安裝openldap:二進制包安裝方法,適用於非Ubuntu的Linux系統,稍微有點麻煩,在安裝OpenlDAP之前還需要安裝Berkeley DB,但配置靈活,可以自定義安裝路徑什麼的。後面的配置也沒說清楚,主要看安裝方法。
    4)Linux服務器部署系列之七—OpenLDAP篇:另一篇較詳細的二進制安裝方法及配置。
    4)Windows下OpenLDAP的安裝及使用:介紹了LDAP的一些基礎知識和Windows下安裝方法。
    5)圖文介紹openLDAP在windows上的安裝配置:比較詳細,值得一看。

上面給出的這幾個鏈接雖然還不錯,但還是欠缺了些什麼?對,就是講解,網上給出的教程都是手把手教你如何安裝和配置,而沒有說明版本差異、具體配置的含義及爲什麼這樣配置,如果因爲版本或環境差異,你按其方法配置不成功,你也不知道哪裏出的問題,因此建議還是先熟悉LDAP的基礎知識,配置文件含義然後再試着安裝。

  1. OpenLDAP疑惑解答
    下面根據我自己的經驗,給出幾個安裝和配置注意事項,供參考。

疑惑1:細心的人會發現有的教程說要配置主機DNS,添加與LDAP相關的域名,而大部分教程都沒有提及這個,那麼到底要不要配置呢?
解答:當然需要配置。安裝好OpenLDAP後首先需要配置slapd.conf這個文件,其中裏面有

suffix "dc=example, dc=com"

這樣一句需要自己配置,這兩個dc代表什麼意思呢?其實dc就是“domainComponent”,也就是域名的組成部分,準確的說是主機域名的後綴組成部分,如果這裏的配置與你的主機域名不對應的話,服務一般是啓動不了的。那麼怎麼配置域名呢?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
那麼相應的我就需要在slapd.conf裏這樣配置suffix:

suffix "dc=alexia, dc=cn"

當然這裏域名後綴不一定只有兩級,也可以是hostname.example.com.cn,然後suffix就應該是“dc=example, dc=com, dc=cn”,這隨便你怎麼設置了,只要對應就行。

疑惑2:很多版本的slapd.conf裏默認都配置了下面兩個變量:

modulepath /usr/lib/ldap
moduleload back_@BACKEND@

這是什麼意思?需要改動嗎?
解答:這是數據庫database的backend,一般slapd.conf裏配置的database都是bdb,也就是Berkeley DB,有的也許是hdb,其實也是Berkeley DB,只是兩個不同的存儲引擎(就像Mysql有MyISAM和InnoDB兩個不同的存儲引擎一樣)。而modulepath和moduleload指定了動態模塊路徑及動態裝載的後端模塊,因爲OpenLDAP默認是用Berkeley DB存儲數據的,如果你有動態的數據需要裝載,那麼就需要配置這兩個參數,對於一般用戶將這兩個註釋掉即可。

疑惑3:OpenLDAP默認採用Berkeley DB存儲數據,那麼可以換用其它的關係數據庫嗎?具體如何配置呢?
解答:當然可以。首先需要明確ldap數據模型來自RDBMS(關係數據庫模型),而並沒有指定一定是哪個DB,只要是關係數據庫都可以作爲LDAP的後臺,那麼你爲什麼會想用其它的數據庫代替自帶的Berkeley DB呢?我想可能是性能相關了,對於少量數據你用哪個都可以,但若涉及到稍大點的數據,比如成千上萬的用戶查詢,那麼Berkeley DB的性能就不可觀了,而且Berkeley DB管理起來也不太方便,畢竟對這個數據庫熟悉的人不多,如果能換作我們經常使用的數據庫,不僅性能得到提升,管理起來也十分容易,豈不是一舉多得。
具體怎麼配置了,請參考這篇文章:用postgresql作後臺的openldap,以PostgreSQL作爲例子進行講解。

疑惑4:新舊版本的OpenLDAP到底有什麼差異呢?
解答:簡單一句話就是:舊版本的OpenLDAP配置文件一般是slapd.conf(路徑可能是/etc/openldap,也可能是/usr/local/openldap,甚至可能是/usr/share/slapd/,不同版本不同安裝不同系統都可能不同,可使用locate slapd.conf進行查找正確的路徑),而新版本(我測試的新版本是2.4.31)的OpenLDAP服務運行時並不會讀取該配置文件,而是從slapd.d目錄(一般與slapd.conf在同一目錄下)中讀取相關信息,我們需要把該目錄下的數據刪掉,然後利用我們在slapd.conf裏配置的信息重新生成配置數據。這也可能是你啓動服務後運行ldap相關命令卻出現“ldap_bind:
Invalid credentials (49)”錯誤的主要原因。具體怎麼重新生成配置數據請看參考資料。

疑惑5:自定義的ldif數據文件中的objectclass後的domain、top、organizationalUnit、inetOrgPerson等等都是什麼意思,可以隨便寫嗎?
解答:存儲LDAP配置信息及目錄內容的標準文本文件格式是LDIF(LDAP Interchange Format),使用文本文件來格式來存儲這些信息是爲了方便讀取和修改,這也是其它大多數服務配置文件所採取的格式。LDIF文件常用來向目錄導入或更改記錄信息,這些信息需要按照LDAP中schema的格式進行組織,並會接受schema 的檢查,如果不符合其要求的格式將會出現報錯信息。因此,ldif文件中的屬性都定義在各大schema中,其中objectclass是對象的類屬性,不能隨便填寫,而應與schema中一致。一般slapd.conf文件的頭部都包含了這些schema:

include ../etc/openldap/schema/core.schema
include ../etc/openldap/schema/cosine.schema
include ../etc/openldap/schema/inetorgperson.schema
include ../etc/openldap/schema/nis.schema
include ../etc/openldap/schema/krb5-kdc.schema
include ../etc/openldap/schema/RADIUS-LDAPv3.schema
include ../etc/openldap/schema/samba.schema

其中前三個是比較重要的schema,定義了我們所需要的各個類,比如ldif中一般先定義一個根節點,其相應的objectclass一般是domain和top,而根節點下的ou屬性即定義組節點(group)的objectclass一般是organizationalUnit,group下可以是group也可以是用戶節點,用戶節點的objectclass一般是inetOrgPerson。而各個節點的一系列屬性如用戶節點的uid、mail、userPassword、sn等等都定義在schema中相關的objectclass裏,可以自己查找看看。

疑惑6:OpenLDAP認證用戶uid時默認是不區分大小寫的,也就是“alexia”與“AleXia”是同一個用戶,在有些情況下這並不合理,能配置使得認證時能區分大小寫嗎?
解答:以我目前的經驗來看,舊版本的OpenLDAP是可以配置區分大小寫的,而新版本的OpenLDAP卻配置不了。爲什麼這麼說呢?
這裏就涉及到“matching rules”這個概念了,即匹配規則,就是各個屬性按什麼樣的規則進行匹配,比如是否區分大小寫、是否進行數字匹配等等,這裏有詳細的官方匹配規則描述。比如舊版本的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屬性匹配時不區分大小寫,如果我們將其改爲caseExactMatch和caseExactSubstringsMatch就表示用戶uid認證時需要區分大小寫,也就是“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} )

可惜是註釋掉的,那我們取消註釋然後改屬性行不行呢?答案是不行,會報錯:Duplicate attributeType: "2.16.840.1.113730.3.1.217”,也就是說該屬性已經被定義了,然後我就去包含的所有schema中搜索uid屬性的定義,結果卻找不到定義,那麼爲什麼還會報這個錯誤呢?後來一陣搜索,終於在這個帖子“slapd:
built-in schema for uidNumber/gidNumber does not have ordering directive”知道了答案,原來新版本的OpenLDAP已經把uid屬性定義schema硬編碼到了slapd程序中,也就是無法在配置文件中修改了,真是坑!
針對這個問題,我給出兩個不太好的解決方案:
下載OpenLDAP源碼,找到定義uid屬性匹配規則的地方,修改它然後重新編譯。這個工作量不輕鬆,熱愛研究源碼的人可以嘗試。不要用uid屬性進行認證,我們可以自定義一個與用戶一一對應的屬性如user-id(不要與已有的屬性重複就行),其配置與uid一模一樣(即模仿uid),然後用該屬性作爲認證的因子,建議重新建一個schema,然後配置好後include進slapd.conf中重啓服務即可。具體怎麼定義和配置可以參考這篇文章。
我的主要經驗也就這些。OpenLDAP也有客戶端,如果你配置成功後,可以用客戶端或寫Java程序進行驗證。

OpenLDAP客戶端

OpenLDAP既有圖形客戶端也有網頁客戶端。

  1. 圖形客戶端
    主要有兩個圖形客戶端:LdapBrowser282 (下載:LdapBrowser282.zip,下載解壓後直接雙擊:lbe.bat 文件即可運行)和LdapAdmin(官方下載),使用都非常簡單。

如下是兩個客戶端的界面,都需要先建立一個鏈接,填上相應的IP地址、端口和dn配置,然後連接即可獲得你配置的數據。

LDAP Browser客戶端:

LDAP Admin客戶端:

  1. 網頁客戶端
    即 phpLDAPadmin,基於PHP的一個web應用,需要配置Apache服務器和PHP,具體的配置方法可參考“phpLDAPadmin 安裝配置講解,通過 Web 端來管理您的 LDAP 服務器”,我比較偷懶,直接使用的PHPnow全套服務,安裝成功後大概是下面這樣一個界面:

使用Java完成LDAP身份驗證

下面借鑑網上資料提供一個簡單的認證程序如下:

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=Tester,dc=alexia,dc=cn"; // 根據自己情況進行修改
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";  // 根,根據自己情況修改
    env.put(Context.SECURITY_PRINCIPAL, root);   // 管理員
    env.put(Context.SECURITY_CREDENTIALS, "123456");  // 管理員密碼

    try {
        ctx = new InitialLdapContext(env, connCtls);
        System.out.println( "認證成功" );  

    } catch (javax.naming.AuthenticationException e) {
        System.out.println("認證失敗:");
        e.printStackTrace();
    } catch (Exception e) {
        System.out.println("認證出錯:");
        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("查找用戶時產生異常。");
        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( "該用戶認證成功" ); 

    }
}

}
既可以作爲普通程序的認證,也可以通過輸出檢查自己的配置是否正確。

LDAP擴展

LDAP的實現除了OpenLDAP外,還有其它,比如OpenDJ(Open source Directory services for the Java platform),它是一個新的LDAPv3相容目錄服務,爲Java平臺開發,提供了一個高性能的,高度可用和安全的企業管理的身份商店。其簡單的安裝過程中,結合了Java平臺的力量,使OpenDJ簡單和最快的目錄服務器部署和管理。有興趣的可以查閱相關資料。

參考資料
LDAP快速入門執行ldapadd 命令時報錯:ldap_bind: Invalid credentials (49) OpenLdap 簡易教程OpenLDAP學習筆記OpenLDAP guide: Schema Specification使用java完成ldap身份驗證


作者:小敏紙
來源:CSDN
原文:https://blog.csdn.net/lanxuezaipiao/article/details/23659947
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章