1 J2EE Security 和 LDAP Security
2 JAAS和WebLogic Security Framework
3 了解WebLogic LDAP Authentication Provider
4 定制自己的Custom LDAP Authentication Provider
5 部署中的注意事項
6 結束語(yǔ)
7 參考資料
從WebLogic Server 7.0開(kāi)始,WebLogic Server的安全機制有了全面的改變,實(shí)現了一個(gè)更加規范的基于JAAS的Security Framework,以及提供了一系列設計良好的Security Service Provider Interface。這樣我們可以根據自己的具體需求,通過(guò)Custom Security Authentication Provider來(lái)實(shí)現安全上的定制功能。
本文將以WebLogic(WebLogic Server 8.1) Security和 LDAP為基礎,介紹Custom LDAP Authentication Provider如何給我們帶來(lái)更多的靈活性,和系統安全設計上更多的空間;以及討論如何實(shí)現一個(gè)Custom LDAP Authentication Provider和部署過(guò)程中的一些良好經(jīng)驗。
由于本文涉及到的范圍太廣,不可能一一詳細討論;為了使沒(méi)有相關(guān)基礎的讀者也能夠閱讀理解本文,因此我將在文章前半部分,試圖通過(guò)最簡(jiǎn)潔扼要的描述,來(lái)使大家對于J2EE Security,WebLogic Security Framework以及LDAP 等有一個(gè)初步的清晰認識;進(jìn)而可以開(kāi)發(fā)出自己的LDAP Authentication Provider。因此很多地方做了比較有限的描述或者介紹,更多詳細的內容可以參考文后附帶的參考資料或者文中給出的鏈接。
1 J2EE Security 和 LDAP Security
Sun J2EE推出以來(lái),其安全部分的規范就一直倍受關(guān)注。我們最常見(jiàn)到安全規范的兩個(gè)方面分別是Servlet Security 和 EJB Security。目前絕大多數的Servlet容器,J2EE容器都能很好的支持這些安全規范。
WebLogic Server作為業(yè)界領(lǐng)先的J2EE服務(wù)器對J2EE Security的支持是非常優(yōu)秀的。我們這里將結合WebLogic Security和使用越來(lái)越廣泛的LDAP做一個(gè)簡(jiǎn)要的介紹,這些是設計開(kāi)發(fā)Custom LDAP Authentication Provider的技術(shù)基礎。
1.1 Authentication 和Authorization
這里需要大家先明確安全上的兩個(gè)重要名詞:一個(gè)是認證(Authentication),一個(gè)是授權(Authorization)。認證是回答這個(gè)人是誰(shuí)的問(wèn)題,即完成用戶(hù)名和密碼的匹配校驗;授權是回答這個(gè)人能做什么的問(wèn)題。我們討論的J2EE Security包括Declarative Authorization和Programmatic Authorization,即一個(gè)是通過(guò)web.xml,ejb-jar.xml等部署描述符中的安全聲明通過(guò)容器提供的服務(wù)來(lái)完成權限控制的;一個(gè)是通過(guò)HttpServletRequest.isUserInRole()和EJBContext.isCallerInRole()這樣的編程接口在應用中自己完成權限控制的。
1.2 資源(Resource)和Security Role
資源原本只包括 Web Resource和EJB Resource,但在WebLogic Security中擴展到幾乎任何一個(gè)WebLogic Platform中的資源,具體可以參考http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777。授權就是針對資源的訪(fǎng)問(wèn)控制。
J2EE Security是基于Security Role的。我們可以將一組資源與一個(gè)Security Role進(jìn)行關(guān)聯(lián)來(lái)達到控制的目的——只有擁有該Role權限的用戶(hù)才能夠訪(fǎng)問(wèn)這些資源。簡(jiǎn)單的說(shuō),我們可以通過(guò)給用戶(hù)分配不同的Security Role來(lái)完成權限的控制。復雜的情況下包括用戶(hù)/用戶(hù)組,以及Principal和Role的映射關(guān)系等等。下面是一個(gè)聲明性安全在web application(war包中WEB-INF/web.xml)中的示例:
| <web-app> <security-constraint> <web-resource-collection> <web-resource-name>Success</web-resource-name> <url-pattern>/welcome.jsp</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>webuser</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>default</realm-name> </login-config> <security-role> <role-name>webuser</role-name> </security-role> </web-app> |
只有擁有角色webuser的用戶(hù)才能夠訪(fǎng)問(wèn)welcome.jsp頁(yè)面,否則容器會(huì )返回401無(wú)權訪(fǎng)問(wèn)的錯誤。更多信息請參考http://e-docs.bea.com/wls/docs81/security/index.html。
同時(shí)我們需要在weblogic.xml(war包中WEB-INF/weblogic.xml)中對security role和principal進(jìn)行映射關(guān)系的配置:
| <weblogic-web-app> <security-role-assignment> <role-name>PayrollAdmin</role-name> <principal-name>Tanya</principal-name> </security-role-assignment> </weblogic-web-app> |
這樣擁有Principal “Tanya”的用戶(hù)(Principal將封裝到Subject中,用戶(hù)將和Subject關(guān)聯(lián))將會(huì )擁有PayrollAdmin的權限。
注意:一般情況下為了簡(jiǎn)化設計,本文中將假設security role即是principal name(如果不配置security-role-assignment,WebLogic會(huì )默認做此假設)。即上例中Principal-name也為PayrollAdmin。
1.3 LDAP Security
LDAP是輕量級目錄服務(wù)(Lightweight Directory Access Protocol)。越來(lái)越多的應用開(kāi)始采用LDAP作為后端用戶(hù)存儲。在安全上,LDAP Security是基于A(yíng)CL(Access Control List)的,它通過(guò)給一個(gè)用戶(hù)組分配LDAP 操作資源(比如對一個(gè)子樹(shù)的查詢(xún),修改等)來(lái)最終完成權限的控制。因此在LDAP中,授權工作是以用戶(hù)組為單位進(jìn)行的。一個(gè)用戶(hù)組一般來(lái)說(shuō)是擁有如下一組屬性的LDAP Entry:

其中objectclass可以為groupOfUniqueNames或者groupOfNames,它們對應的組成員屬性分別是uniquemember和member。如果是動(dòng)態(tài)組,objectclass為groupOfURLs。動(dòng)態(tài)組一般應用在成員可以通過(guò)某種業(yè)務(wù)邏輯運算來(lái)決定的情況下。比如,經(jīng)理為ZHANGSAN的全部員工。下面是一個(gè)典型的動(dòng)態(tài)組,memberURL屬性定義了哪些entry屬于該組:

從圖1-3-1中我們可以看出,用戶(hù)WANTXIAOMING,ZHANGSAN,LISI屬于組HR Managers。這種組和成員的關(guān)系是通過(guò)屬性uniquemember來(lái)決定的。同時(shí)LADP Group 支持嵌套,即一個(gè)組可以是另外一個(gè)組的成員,比如我們將Accounting Managers組分配給HR Managers組作為其成員:

這樣將表示Accounting Managers中的成員,同時(shí)也是組HR Managers的成員。通過(guò)這種層級關(guān)系可以使權限分配變的更加靈活。
下面是一些名詞的解釋?zhuān)M蠹覍DAP有更好的理解:
a) Objectclass —— LDAP對象類(lèi),抽象上的概念類(lèi)似與一般我們理解的class。根據不同的objectclass,我們可以判斷這個(gè)entry是否屬于某一個(gè)類(lèi)型。比如我們需要找出LDAP中的全部用戶(hù):(objectclass=person)再比如我們需要查詢(xún)全部的LDAP組:(objectclass=groupOfUniqueNames)
b) Entry —— entry可以被稱(chēng)為條目,或者節點(diǎn),是LDAP中一個(gè)基本的存儲單元;可以被看作是一個(gè)DN和一組屬性的集合。 屬性可以定義為多值或者單值。
c) DN —— Distinguished Name,LDAP中entry的唯一辨別名,一般有如下的形式:uid=ZHANGSAN, ou=staff, ou=people, o=examples。LDAP中的entry只有DN是由LDAP Server來(lái)保證唯一的。
d) LDAP Search filter ——使用filter對LDAP進(jìn)行搜索。 Filter一般由 (attribute=value) 這樣的單元組成,比如:(&(uid=ZHANGSAN)(objectclass=person)) 表示搜索用戶(hù)中,uid為ZHANGSAN的LDAP Entry.再比如:(&(|(uid= ZHANGSAN)(uid=LISI))(objectclass=person)),表示搜索uid為ZHANGSAN, 或者LISI的用戶(hù);也可以使用*來(lái)表示任意一個(gè)值, 比如(uid=ZHANG*SAN),搜索uid值以 ZHANG開(kāi)頭SAN結尾的Entry。更進(jìn)一步,根據不同的LDAP屬性匹配規則,可以有如下的Filter: (&(createtimestamp>=20050301000000)(createtimestamp<=20050302000000)),表示搜索創(chuàng )建時(shí)間在20050301000000和20050302000000之間的entry。
Filter中 “&” 表示“與”;“!”表示“非”;“|”表示“或”。根據不同的匹配規則,我們可以使用“=”,“~=”,“>=”以及“<=”,更多關(guān)于LDAP Filter讀者可以參考LDAP相關(guān)協(xié)議:http://www.ietf.org/rfc/rfc2254.txt。
e) Base DN —— 執行LDAP Search時(shí)一般要指定basedn,由于LDAP是樹(shù)狀數據結構,指定basedn后,搜索將從BaseDN開(kāi)始,我們可以指定Search Scope為:只搜索basedn(base),basedn直接下級(one level),和basedn全部下級(sub tree level)。
下面是一個(gè)典型的LDAP Tree結構,右側顯示Entry uid=ZHANGSAN, ou=staff, ou=people, o=examples的屬性,該entry代表了一個(gè)名字叫張三的用戶(hù):

2 JAAS和WebLogic Security Framework
現在越來(lái)越多的人開(kāi)始了解JAAS,使用JAAS。WebLogic Security Framework就是基于JAAS的。因此我們需要對此有一個(gè)非常準確的理解才能夠設計開(kāi)發(fā)Custom Authentication Provider。
下面我們從幾個(gè)名詞入手,了解JAAS和 WebLogic Security Framework的關(guān)鍵之處:
2.1 Principal,Subject和LoginModule
a) Principal
當用戶(hù)成功驗證后,系統將會(huì )生成與該用戶(hù)關(guān)聯(lián)的各種Principal。我們這里將Principal進(jìn)行簡(jiǎn)化的設計,認為一個(gè)Principal就是用戶(hù)登錄賬號和它所屬于的組(LDAP Group)。這樣當用戶(hù)登錄成功后,我們將會(huì )在LDAP中執行搜索,找出用戶(hù)屬于哪些組,并將這些組的名字,或者其標識作為Principal返回。這樣,當用戶(hù)在LDAP中屬于某一個(gè)組,并且這個(gè)組的名字對應到 web.xml (或者ejb-jar.xml)中的Security role,那么這個(gè)用戶(hù)就可以看作擁有訪(fǎng)問(wèn)這個(gè)Security Role定義的資源的權限。
在WebLogic Security Framework中,這個(gè)LDAP Group的名字(Principal)和Security Role的映射關(guān)系,可以通過(guò)一個(gè) Role Mapping Provider來(lái)實(shí)現動(dòng)態(tài)的匹配,即用戶(hù)的動(dòng)態(tài)權限控制。比如在運行時(shí)根據某一個(gè)業(yè)務(wù)邏輯來(lái)決定用戶(hù)是否擁有某一個(gè)權限。關(guān)于Role Mapping Provider,讀者可以參考下面鏈接的內容:http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542。
b) Subject
JAAS規定由Subject封裝用戶(hù)以及用戶(hù)認證信息,其中包括Principals。下面是WebLogic Security Framework中Subject的組成圖示:

這樣當用戶(hù)試圖訪(fǎng)問(wèn)一個(gè)受限的J2EE資源時(shí),比如一個(gè)web URL,或者一個(gè) EJB Method(可以在web.xml或者ejb-jar.xml中定義,由Security Role控制),WebLogic Security Framework將會(huì )通過(guò) Authorization Provider檢查用戶(hù)當前的Subject中是否包含有是否可以訪(fǎng)問(wèn)受限資源的Principals。由于Principals將和J2EE Security Role在weblogic.xml中定義一個(gè)映射關(guān)系(或者通過(guò)其他業(yè)務(wù)邏輯來(lái)確定這種關(guān)系),因此通過(guò)這樣的關(guān)系,可以最終知道用戶(hù)是否有某一個(gè)J2EE Resource的訪(fǎng)問(wèn)權限。
c) LoginModule
JAAS LoginModule是一個(gè)Authentication Provider必須的組成部分。LoginModule是認證的核心引擎,它負責對用戶(hù)身份進(jìn)行驗證,同時(shí)將返回與用戶(hù)關(guān)聯(lián)的Principals(用戶(hù)登錄賬號,以及LDAP Groups),然后放入Subject中,供后續的訪(fǎng)問(wèn)控制使用。
我們將在LoginModules中完成LDAP的相關(guān)認證,查詢(xún)操作,將用戶(hù)在LDAP中所屬于的組搜索出來(lái),作為認證后的結果封裝到Subject中返回。
2.2 WebLogic Authentication認證過(guò)程
下面我們了解一下WebLogic的認證過(guò)程。以下圖片來(lái)自http://e-docs.bea.com/wls/docs81/dvspisec/atn.html 我將其中主要部分進(jìn)行說(shuō)明。

Security Framework在WebLogic Server啟動(dòng)時(shí)初始化Authentication Provider(5)。當有認證請求進(jìn)入時(shí),Security Framework首先將通過(guò)AuthenticationProvider.getLoginModuleConfiguration()來(lái)獲取一個(gè)AppConfigurationEntry對象。通過(guò)AppConfigurationEntry(詳見(jiàn)http://java.sun.com/security/jaas/apidoc/javax/security/auth/login/AppConfigurationEntry.html )可以初始化一個(gè)LoginModule。初始化LoginModule的方法為:public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options),可以看到里面有Subject, CallbackHandler等等重要參數。
被實(shí)例化的JAAS LoginModule將完成用戶(hù)的一系列驗證任務(wù)。驗證完成后,在(6a)中principals將被Principal Validation Provider簽名,在( 6b)中存放到與用戶(hù)關(guān)聯(lián)的Subject里面。Security Framework最后將拿著(zhù)該用戶(hù)的Subject去完成其他權限控制等任務(wù)。
3 了解WebLogic LDAP Authentication Provider
現在我們有信心了解一下WebLogic LDAP Authentication Provider的工作原理了。這里將以WebLogic提供的iPlanet Authentication Provider的配置為例進(jìn)行說(shuō)明。在這里也需要明確說(shuō)明一下,為了方便進(jìn)行描述,我們將實(shí)際屬于LoginModule的行為也一并歸結到Provider中。沒(méi)有單獨將兩個(gè)的行為分開(kāi),目的是為了突出整個(gè)完整的過(guò)程。
3.1 iPlanet Authentication Provider配置

從上圖可以看出我們需要指定LDAP服務(wù)器的地址,端口,連接LDAP使用的Principal(不同于前面討論的Principal,這個(gè)Principal實(shí)際是一個(gè)連接LDAP的用戶(hù),也就是一個(gè)LDAP 中用戶(hù)Entry的 DN,它必須要有相關(guān)的LDAP 搜索等權限)和Credential(一般來(lái)說(shuō)就是口令)。
再看下面關(guān)于Users的配置:

3.1.1 User Object Class —— 前面已經(jīng)對objectclass進(jìn)行過(guò)說(shuō)明,指明LDAP Entry屬于哪一類(lèi)
3.1.2 User Name Attribute —— 用戶(hù)登錄賬號在LDAP Entry中的屬性,一般為UID或者cn
3.1.3 User Base DN —— 所有的用戶(hù)將會(huì )放置到這個(gè)子樹(shù)下面,因此在Provider中對用戶(hù)進(jìn)行的搜索將會(huì )從這個(gè)basedn開(kāi)始
3.1.4 User Search Scope —— 指定搜索范圍為Basedn的直接一級或者全部下級
3.1.5 User From Name Filter —— 使用這個(gè)filter可以搜索出用戶(hù)信息。其中%u將會(huì )被用戶(hù)輸入的登錄賬號替換,從而查詢(xún)中LDAP中的用戶(hù)信息

3.1.6 Group Base DN —— 從該Base DN開(kāi)始搜索用戶(hù)組
3.1.7 Group From Name Filter —— %g將會(huì )被組的名字替換,通過(guò)該filter可以搜索出符合條件的LDAP Group
3.1.8 Static group name attribute —— 組名字的屬性,屬性cn對應的值就是組的名字

3.1.9 Static Member DN Attribute —— 靜態(tài)成員屬性,通過(guò)該屬性可以判斷一個(gè)Entry是否屬于一個(gè)組
3.1.10 Static Group DNs From Member DN Filter —— 通過(guò)該filter可以找出用戶(hù)屬于哪些組
3.1.11 Dynamic Group —— 動(dòng)態(tài)組是在運行時(shí)根據某種業(yè)務(wù)邏輯,來(lái)決定成員隸屬關(guān)系的LDAP Group
3.2 iPlanet Authentication Provider的工作原理
從上面配置的介紹中可以看出,后端存儲為L(cháng)DAP的情況下,在Console中我們需要配置的參數已經(jīng)清楚的表明它所要完成工作的內容。
首先,它使用配置的User BaseDN和Filter,來(lái)根據用戶(hù)輸入的登錄賬號進(jìn)行搜索,找出存放在LDAP中的用戶(hù)Entry。如果找到一個(gè)用戶(hù),那么Provider就使用該用戶(hù)的DN和用戶(hù)輸入的口令,進(jìn)行驗證。驗證可以使用LDAP Bind和LDAP Compare,這需要根據不同LDAP的特點(diǎn)來(lái)進(jìn)行選擇。
A. LDAP Bind —— 將當前的LDAP Connection綁定到一個(gè)用戶(hù)身份上。這樣后續的使用該Connection的LDAP Operation都將以該身份進(jìn)行。LDAP Bind需要兩個(gè)重要參數,一個(gè)是用戶(hù)Entry的DN,一個(gè)是該用戶(hù)的口令。
B. LDAP Compare —— LDAP Compare是一個(gè)為兼容X.500的古老操作,它用于檢查一個(gè)屬性值是否包含在指定Entry中的屬性里。這樣我們可以在知道用戶(hù)password存放在哪個(gè)屬性的前提下,對該屬性進(jìn)行compare。如果成功,表明口令正確。如果屬性值為散列后的口令(絕大多數情況),有的LDAP Server支持這樣的驗證,有的不支持,比如iPlanet LDAP Server 5。
驗證成功后,Provider將使用Console中關(guān)于Groups和Memberships中的配置,查找用戶(hù)屬于哪些LDAP Group,而且由于這些組本身可能會(huì )有一些嵌套,因此對于搜索到的組還需要進(jìn)行查詢(xún)。即使用filter: (&(uniquemember=uid=ZHANGSAN,ou=staff,ou=people,o=examples)(objectclass=groupOfUniqueNames))從Group Base DN開(kāi)始搜索,將返回用戶(hù)所屬的第一層次Group;然后對于這些返回的組DN,仍然需要使用上面的Filter進(jìn)行搜索(uniquemember值替換為組的DN),找出嵌套關(guān)系,直到查詢(xún)完成沒(méi)有組嵌套為止(此處需要防止陷入嵌套的循環(huán)中,比如Group A 包含了Group B; Group B又包含了Group A,有的LDAP Server可以自動(dòng)檢測出,有的需要我們程序來(lái)判斷)。
然后將用戶(hù)登錄的賬號,用戶(hù)所屬組的名字(屬性CN的值或其他),放入Subject中。最后調用Principal Validator Provider,對Subject中的principals進(jìn)行簽名,來(lái)表明該Subject是這個(gè)Provider生成的。這樣防止其他攻擊者偽造Subject以及Principal進(jìn)行欺騙。此處也可以解釋為何在不同的WLS Domain間不能夠傳遞Subject,我們可以通過(guò)設置域信任來(lái)完成這種Subject的傳遞。設置域信任使用的Credential就是簽名用的Key。
圖3-1-5表明了如何設置WebLogic Domain Credential,默認情況下WebLogic Server會(huì )在啟動(dòng)的時(shí)候隨即生成一個(gè)Credentials(在WLS6.1時(shí),這個(gè)值就是system用戶(hù)的口令):

可以想見(jiàn)如果我們實(shí)現自己的Principal Validator Provider,讓它去一個(gè)集中的驗證服務(wù)器中對Subject進(jìn)行簽名,或者驗證Subject,這樣就可以實(shí)現域信任,進(jìn)而完成Application(EJB Tier)層的SSO。
通過(guò)以上的討論,我們對于實(shí)現自己的LDAP Authentication Provider是不是又增加了一份信心?
4 定制自己的Custom LDAP Authentication Provider
為何要定制自己的Authentication Provider? 由于WebLogic Server已經(jīng)提供了很多默認的Authentication Provider在一般情況下我們確實(shí)沒(méi)有必要實(shí)現自己的Provider。但是面對某些針對安全方面的復雜需求時(shí),WebLogic Server提供的Provider很有可能不滿(mǎn)足這些需求,此時(shí)就需要我們定制自己的Provider。
在這一章的開(kāi)頭部分中我需要簡(jiǎn)要討論關(guān)于WebLogic MBean Types,以及WebLogic Console擴展等內容,目的在于讓讀者了解到我們通過(guò)WebLogic Console可以完成對Custom Security Provider的配置和部署,我將以WebLogic 提供的Sample Security Provider為示例進(jìn)行說(shuō)明。詳細的信息可以參考以下的一些資源:
http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106272
http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106241
上面兩個(gè)鏈接描述了如何創(chuàng )建MBean Types以及在控制臺上配置Custom Authentication Provider.下面這個(gè)鏈接中專(zhuān)門(mén)介紹了WebLogic Console的擴展:
http://dev2dev.bea.com.cn/techdoc/webser/2005012102.html
讀者可以從http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp下載WLS提供的Sample Security Provider。
4.1 MBean Types和WebLogic Console
一般情況下,人們可能更習慣通過(guò)WebLogic Console對Security Provider進(jìn)行配置。這里我將簡(jiǎn)要描述這個(gè)過(guò)程,以及可以到達的一個(gè)效果。限于篇幅就不詳細討論了。
從weblogic.management.security.authentication.Authenticator擴展MBean Types。 MBean Types是MBean(http://java.sun.com/products/JavaManagement/wp/)的工廠(chǎng),我們擴展SampleSecurityProviders81 包中的SimpleSampleAuthenticator.xml(MBean Definition File),增加一個(gè)我們自定義的參數LDAP Server IP:
| <MBeanAttribute Name = "LDAPServerIP" Type = "java.lang.String" Writeable = "true" Default = ""127.0.0.1"" /> |
這樣在Provider中我們將通過(guò)MbeanMaker(WLS提供)生成的SimpleSampleAuthenticatorMBean中取到這個(gè)屬性:SimpleSampleAuthenticatorMBean.getLDAPServerIP()。MBean將在初始化Provider的時(shí)候作為參數傳入。這樣我們就可以通過(guò)MBean中的參數控制Provider的行為。
當然這個(gè)參數是可以在WebLogic Console中設置的,通過(guò)對MBean Types的擴展,在WebLogic Console上看到的畫(huà)面如下:

這樣我們可以通過(guò)Console修改配置參數(修改的Security Provider參數將保存在config.xml中,默認的值將保存在MBean Jar File中)。
4.2 為何定制LDAP Authentication Provider
當我們面臨越來(lái)越復雜的安全方面的業(yè)務(wù)需求時(shí),或者面臨較高的性能要求,需要根據目標LDAP做針對性的優(yōu)化時(shí),或者需要將我們已有的認證,或授權模塊集成到WebLogic平臺時(shí),WebLogic提供的現成的Provider往往不能滿(mǎn)足我們的需求。
4.2.1 復雜的業(yè)務(wù)需求
當系統要求用戶(hù)不僅僅輸入用戶(hù)名(j_username),口令(j_password),還需要輸入其他信息,比如登錄的地點(diǎn),系統的名字,用戶(hù)的類(lèi)型等等。如果是采用基于J2EE Form的驗證方式, 登錄信息需要提交到j(luò )_security_check(Servlet規范定義由容器負責實(shí)現的Servlet),導致我們沒(méi)法處理更多的信息。
這個(gè)時(shí)候,如果能夠實(shí)現我們自己的 Authentication Provider,那么我們就可以通過(guò)TextInputCallback來(lái)獲取登錄表單中更多的信息了;進(jìn)而通過(guò)這些信息在Provider中完成符合我們需要的處理。
比如搜狐的登錄頁(yè)面上需要選擇用戶(hù)的類(lèi)型:

4.2.2 性能需求或者調優(yōu)
有時(shí),有的用戶(hù)會(huì )比較困惑為何WebLogic LDAP Security Provider在壓力測試中的表現不很理想,用戶(hù)需要較長(cháng)時(shí)間的等待,才能夠登錄到系統中。由于這些Provider是 WebLogic產(chǎn)品的一部分,因此缺乏對不同目標LDAP Server的有針對性的優(yōu)化。這樣就使得我們無(wú)法充分發(fā)揮具體LDAP Server的性能調優(yōu)。
比如,有的LDAP Server支持動(dòng)態(tài)組(LDAP Dynamic Group,成員關(guān)系是運行時(shí)根據ldap server,basedn,filter等動(dòng)態(tài)決定的),可以使用如下的Filter查詢(xún)用戶(hù)屬于哪些動(dòng)態(tài)組:
(uniquemember=uid=MAXQ,ou=staff,ou=people,o=examples)
有的LDAP Server雖然支持動(dòng)態(tài)組,但是支持的有限,不能使用上面的Filter獲取用戶(hù)屬于哪些動(dòng)態(tài)組。在WebLogic iPlanet Authentication Provider的實(shí)現中,它先是搜索出全部的動(dòng)態(tài)組,然后再遍歷這些動(dòng)態(tài)組,依次去LDAP中檢查用戶(hù)是否屬于一個(gè)組;很明顯,這樣雖然最大程度的滿(mǎn)足了不同LDAP Server的要求(從產(chǎn)品的角度講可能是必須的),但是與LDAP交互的次數大增,并發(fā)用戶(hù)量一大性能下降的比較明顯。
此時(shí),如果系統中的LDAP支持上面的Filter或者有更好的搜索方式,那么完全可以通過(guò)定制Provider完成對性能的優(yōu)化。
4.2.3 已有權限控制的集成
如果系統中已經(jīng)存在了現成的滿(mǎn)足需求的認證模塊,并且已經(jīng)很好的工作;在系統轉向J2EE架構,并使用WebLogic Server做J2EE容器時(shí),我們可能會(huì )更愿意直接在Provider中加入這個(gè)認證模塊。
綜上,我只是列舉了一些可以驅動(dòng)我們開(kāi)發(fā)自己Provider的需求,相信在讀者實(shí)際工作中可能會(huì )面臨更復雜的情況,開(kāi)發(fā)自己的Provider將是一個(gè)非常好的選擇。
4.3 LDAP Authentication Provider實(shí)現
本文之前為了表述的方便沒(méi)有單獨提到LoginModule,認為L(cháng)oginModule的行為就是LDAP Authentication Provider的行為。到了目前的具體實(shí)現階段,我們必須分開(kāi)Authentication Provider和JAAS LoginModule。最終部署到WebLogic上的實(shí)際只是LDAP AuthenticationProvider Implements。
WebLogic SecurityFramework通過(guò)Authentication Provider獲取具體的JAAS LoginModule。通過(guò)LoginModule完成最終登錄的工作。因此我們必須先實(shí)現一個(gè)AuthenticationProvider。
我們一般通過(guò)weblogic.security.spi.AuthenticationProvider 來(lái)實(shí)現自己的AuthenticationProvider。這里介紹其中的幾個(gè)重要方法:
a) public void initialize(ProviderMBean mbean, SecurityServices services)
初始化一個(gè)Provider。通過(guò)參數MBean我們可以獲取到在WebLogic Console中配置的各項參數。進(jìn)而初始化我們的Provider,然后通過(guò)Provider傳遞到LoginModule中。
b) public void shutdown()
釋放一些與Provider,LoginModule等相關(guān)的資源。
c) public AppConfigurationEntry getLoginModuleConfiguration()
這個(gè)方法非常重要,通過(guò)該方法,WebLogic Security Framework可以獲取用于初始化LoginModule的AppConfigurationEntry。AppConfigurationEntry中存放了LoginModule的類(lèi)名等信息,比如使用如下代碼返回一個(gè)AppConfigurationEntry:
| return new AppConfigurationEntry( "examples.security.providers.authentication.SampleLoginModuleImpl", controlFlag, options); |
其中LoginModule Name就
是"examples.security.providers.authentication.SampleLoginModuleImpl",我們通過(guò)它就可以實(shí)例化一個(gè)LoginModule并通過(guò)LoginModule.initialize()方法進(jìn)行初始化。
d) public AppConfigurationEntry getAssertionModuleConfiguration()
該方法將返回一個(gè)與Identity Assertion Provider關(guān)聯(lián)的LoginModule。這個(gè)Assertion LoginModule,將只會(huì )驗證用戶(hù)是否存在,以及如果存在返回用戶(hù)的Principals。 該方法也比較重要,需要正確實(shí)現,比如我們使用CLIENT-CERT這種WEB認證方式,該方法就會(huì )被調用。
Provider的實(shí)現比較簡(jiǎn)單,讀者可以在http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp下載WebLogic提供的Samples,查看SampleAuthenticationProviderImpl的代碼。
4.4 LDAP LoginModule 邏輯流程
實(shí)現了Provider后,必須擁有我們自己的LDAP LoginModule。下面是一個(gè)簡(jiǎn)單的用于演示的驗證邏輯流程圖。實(shí)際的一個(gè)LoginModule由于不同的業(yè)務(wù)需求,情況可能會(huì )復雜得多。這里只是描述了最核心最基本的邏輯,使讀者能有一個(gè)清晰的思路。后面我將以這個(gè)流程為例進(jìn)行實(shí)現。

4.5 LoginModule代碼示例和講解
這里我將使用Netscape LDAP SDK for java作為開(kāi)發(fā)工具實(shí)現LDAP相關(guān)的操作,讀者可以到http://docs.sun.com/db/doc/816-6402-10 下載開(kāi)發(fā)手冊,從http://www.mozilla.org/directory/ 下載SDK 包。一般來(lái)說(shuō)還可以通過(guò)JNDI來(lái)操作LDAP,我個(gè)人認為Sun LDAP JNDI Provider中關(guān)于Connection Pool的實(shí)現非常優(yōu)秀。但不管使用哪種SDK,對LDAP的編程原理上基本都是相同的(因為基于同樣的LDAP協(xié)議),不同的可能僅僅是接口,類(lèi)的名字而已。
4.5.1 初始化Connection Pool
為了有效使用寶貴,并且有限的LDAP連接,必須使用連接池。下面的代碼初始化了一個(gè)LDAP連接池:
| /** * */ static ConnectionPool pool; /** * * LDAPException */ public LdapClient() throws LDAPException { pool= new ConnectionPool( 1, 150, "127.0.0.1", 389, "cn=Directory Manager", "88888888"); } |
Sun JNDI LDAP Service Provider(JDK1.4)中可以通過(guò)在環(huán)境變量中設置具體的參數來(lái)啟用連接池,達到復用連接的目的。具體可以參考鏈接:http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html,下面是示例代碼:
Hashtable env= new Hashtable();
…
env.put("com.sun.jndi.ldap.connect.pool", "true");
DirContext ctx= new InitialDirContext( env);
4.5.2 根據用戶(hù)輸入的登錄賬號,搜索用戶(hù)Entry
下面這個(gè)方法實(shí)現了從LDAP中搜索用戶(hù)Entry DN的最簡(jiǎn)單的過(guò)程。實(shí)際上我們可以在其中實(shí)現很多定制的功能。比如允許用戶(hù)使用多于一個(gè)的賬號登錄,只要這些賬號能夠通過(guò)LDAP Searche最終返回一個(gè)唯一的用戶(hù)DN即可。
| * * LDAPException */ public String getUserDN( String uid) throws LDAPException{ LDAPConnection conn= pool.getConnection(); try { String[] attrs= new String[] {}; LDAPSearchResults sr= conn.search("o=examples", 2, "(uid="+ uid +")", attrs, false); if ( sr.hasMoreElements()) { LDAPEntry entry= sr.next(); return entry.getDN(); } throw new LDAPException("No Such Object:"+ uid, LDAPException.NO_SUCH_OBJECT); }catch ( LDAPException ex) { throw ex; }finally { try { if (conn!= null) pool.close(conn); }catch ( Exception ex) {} } } |
首先需要從池中獲取一個(gè)LDAP連接,然后使用LDAPConnection.search方法進(jìn)行搜索。我這里以一個(gè)典型的LDAP Search接口為例進(jìn)行說(shuō)明,其他API比如JNDI等,基于同樣的LDAP協(xié)議,接口中同樣參數的含義都是相同的。
| public LDAPSearchResults search(java.lang.String base, int scope, java.lang.String filter, java.lang.String[] attrs, boolean attrsOnly) |
1) Base: 表明從該Basedn開(kāi)始搜索,可以通過(guò)MBean獲取
2) Scope: 搜索的范圍:
a) LDAPv2.SCOPE_BASE, 只搜索basedn指定的entry
b) LDAPv2.SCOPE_ONE, 在basedn的下一級entry中搜索,不包括basedn
c) LDAPv3.SCOPE_SUB,在basedn的全部下級entry中搜索,包括basedn
3) Filter:過(guò)慮條件。比如uid=ZHANGSAN,搜索UID為ZHANGSAN的用戶(hù);再比如uid=ZHANG*,搜索 ZHANG開(kāi)頭的賬號;或者uid=Z*S,搜索以Z開(kāi)頭S結尾的賬號。前面已經(jīng)介紹過(guò)這里就不多說(shuō)了。
4) attrs:返回該attrs數組中指定的屬性,比如new String[]{“uid”},只返回屬性uid,其他屬性將會(huì )不在結果中返回。 一般來(lái)說(shuō)我們會(huì )要求開(kāi)發(fā)人員只將需要的屬性返回,這樣避免返回無(wú)用的屬性,降低網(wǎng)絡(luò )和Server等方面的資源開(kāi)銷(xiāo);而且如果存在一個(gè)有較大屬性集合的Entry,并且你并不使用到這個(gè)較大的屬性集合。舉個(gè)實(shí)際例子來(lái)說(shuō),比如你的系統中擁有很多成員數目超過(guò)2萬(wàn)或者更多的LDAP Groups,并且你希望通過(guò)LDAP Search找出某一個(gè)用戶(hù)屬于的組CN,那么搜索結果只返回組的CN已經(jīng)可以滿(mǎn)足你的要求了,這時(shí)就沒(méi)有必要將全部member屬性也返回了。這在后面我會(huì )有代碼來(lái)說(shuō)明。
這里還有一點(diǎn)很重要的細節,相信對讀者會(huì )有幫助。比如,你希望搜索到modifytimestamp等Operational Attributes。這些屬性(LDAP Server自己負責維護的,用戶(hù)無(wú)法修改的)必須要在參數attrs中指定,LDAP Server才會(huì )返回給客戶(hù)端。如果用戶(hù)希望返回全部User Attributes的同時(shí),返回指定的 Operational Attributes那該怎么辦?不在attrs列表中的屬性將不會(huì )被返回,一旦我指定了attrs,是否需要將全部的屬性都列在attrs參數中?實(shí)際上此時(shí)只需要傳入 new String[]{ “*”, “createtimestamp”}這樣的參數即可;“*”號即代表了全部的User Attributes。在Sun JNDI LDAP Service Provider中也是這樣,盡管找遍Sun關(guān)于JNDI的文檔也找不到這樣的說(shuō)明。LDAP協(xié)議對此的說(shuō)明在 http://www.ietf.org/rfc/rfc2251.txt 第28頁(yè)。
5) attrsOnly:只返回屬性名稱(chēng),不包含值。我們一般設置為false。
我們下面看一下Sun JNDI中關(guān)于LDAP Search的接口:
public NamingEnumeration search(String name,
String filter,
SearchControls cons)
throws NamingException
name參數就是上面的basedn,SearchControls中封裝更多的設置,比如:SearchControls. setSearchScope(int scope)其中的scope對應上面的2); SearchControls.setReturningAttributes(String[] attrs),設置指定返回的屬性,對應上面的4)。
其他的參數還包括Size Limit,即限制搜索結果的數目(LDAP返回的Entry一般情況下是沒(méi)有排序的,除非使用一些Sort Control),當你的搜索可能會(huì )返回成千上萬(wàn)的Entry時(shí),限制搜索的數目是非常明智也是必須的。關(guān)于這些,讀者可以參考API文檔或者LDAP協(xié)議。
4.5.3 根據用戶(hù)的Entry DN,和用戶(hù)輸入的口令完成身份驗證
下面的方法實(shí)現了到LDAP的身份驗證。LDAP中的用戶(hù)實(shí)際上就是一個(gè)LDAP Entry,一次驗證實(shí)際是將Connection與這個(gè)Entry進(jìn)行綁定,因此驗證時(shí)我們需要兩個(gè)必須的參數,一個(gè)是Entry的DN,一個(gè)是口令。LDAP的身份驗證方式有多種,包括SASL以及基于證書(shū)的驗證等,我們這里只介紹Simple Authentication。關(guān)于SASL更多信息讀者可以參考:http://www.ietf.org/rfc/rfc2222.txt。
| /** * * dn * pwd * * LDAPException */ public boolean authentication( String dn, String pwd) throws LDAPException { LDAPConnection conn= pool.getConnection(); try { conn.authenticate( dn, pwd); return true; }catch ( LDAPException ex) { if ( ex.getLDAPResultCode()== LDAPException.INVALID_CREDENTIALS) { return false; // 用戶(hù)口令錯誤 } if ( ex.getLDAPResultCode()== LDAPException.NO_SUCH_OBJECT) { return false; // 用戶(hù)不存在 } throw ex; }finally { try { if ( conn!= null) pool.close(conn); }catch ( Exception ex) { } } } |
我們這里使用 LDAP Bind操作完成對用戶(hù)的身份驗證。本文前面曾提到也可以使用LDAP Compare,通過(guò)比較口令屬性(userpassword)中的值來(lái)完成驗證。我們需要根據不同的LDAP Server選擇合適的驗證方法。
比如iPlanet LDAP Server 5,只能通過(guò)Bind完成認證;而Oracle Internet Directory可以通過(guò)LDAP Compare完成驗證,而且還支持口令策略。
如果我們使用了連接池,連接池中的連接一般都是使用權限較大的用戶(hù)初始化的,這樣這些連接才可以完成對LDAP的搜索操作;而當通過(guò)這些連接對普通用戶(hù)進(jìn)行身份驗證時(shí),如果通過(guò)驗證,連接的身份將被改變?yōu)槠胀ǖ挠脩?hù)(或稱(chēng)為與普通用戶(hù)的身份關(guān)聯(lián))。普通用戶(hù)很可能沒(méi)有除了bind以外的任何權限,所以在連接被放入池中前,我們必須要恢復連接的身份。
這樣我們必須執行兩次LDAP Bind,一次用于對普通用戶(hù)驗證身份;一次用于恢復連接的較大權限的用戶(hù)身份。我們看到這樣效率是比較低的,可能你在LDAP Server端統計有2萬(wàn)次bind請求,實(shí)際上只有1萬(wàn)人次登錄。
對于特定的LDAP Server,比如 Oracle Internet Directory,可以通過(guò)LDAP Compare對用戶(hù)身份進(jìn)行驗證,并且不會(huì )改變連接關(guān)聯(lián)的用戶(hù)身份。這樣我在使用池的情況下只需要一次LDAP Compare即可。效率有很大提高。如果不通過(guò)定制LDAP Authentication Provider,這樣的調優(yōu)是沒(méi)法實(shí)現的。
Netscape LDAP SDK的ConnectionPool的實(shí)現中,在連接放入池中前會(huì )檢查連接的身份,如果身份被改變,那么會(huì )重新進(jìn)行bind。所以我們沒(méi)有必要在代碼中再做bind。
4.5.4 根據用戶(hù)的DN搜索用戶(hù)屬于的組列表
有了上面的基礎后,這個(gè)方法就很容易理解了。下面我們來(lái)看看如何返回用戶(hù)所屬的組。
| /** * * groupbasedn * memberDn * * LDAPException */ public List getGroupMembership( String groupbasedn, String memberDn) throws LDAPException { LDAPConnection conn= pool.getConnection(); try { LDAPSearchResults sr= conn.search( groupbasedn, 2, "(uniquemember="+ memberDn +")", new String[] {"cn"}, false); List groups= new java.util.ArrayList(); while ( sr.hasMoreElements()) { LDAPEntry entry= sr.next(); LDAPAttribute attr= entry.getAttribute("cn"); if ( attr!= null) { String[] values= attr.getStringValueArray(); if ( values!= null && values.length>0) groups.add( values[0]); } } return groups; }catch ( LDAPException ex) { throw ex; }finally { try { if ( conn!= null) pool.close(conn); }catch ( Exception ex) { } } } |
成員和組的membership主要是通過(guò)uniquemember或者 member屬性來(lái)定義的。成員不僅僅可以使用用戶(hù),也可以是組,因為組可以嵌套。
a) 上面的方法中只實(shí)現了一個(gè)層次的搜索,即用戶(hù)——組的搜索,而組間的嵌套搜索沒(méi)有實(shí)現。讀者可以根據系統內的具體情況,在此處也可以做一些優(yōu)化。
b) 組成員的數量可能比較大,為了避免不必要的開(kāi)銷(xiāo),我們指定只返回組的cn屬性。
c) 動(dòng)態(tài)組的優(yōu)化。在WebLogic默認的實(shí)現中,Provider(實(shí)際為L(cháng)oginModule)會(huì )不斷的拿動(dòng)態(tài)組中定義的URL中的filter和用戶(hù)的DN去LDAP做搜索,來(lái)看用戶(hù)是否屬于該組。本文前面也討論了,這樣效率很低,完全可以放在Provider本地實(shí)現URL和Entry的匹配。
4.5.5 LoginModule中的login()方法實(shí)現
一切準備就緒后,我們就可以完成LoginModule.login()這個(gè)最核心的方法了。下面我根據代碼中的注釋逐條說(shuō)明。
| // A boolean loginSucceeded; // B List principals= new java.util.ArrayList(); /** * * * LoginException */ public boolean login() throws LoginException { // C Callback[] callbacks= new Callback[] { new NameCallback("username: "), new PasswordCallback("password: ",false)}; try { callbackHandler.handle( callbacks); }catch (IOException e) { throw new LoginException(e.toString()); }catch (UnsupportedCallbackException e) { throw new LoginException(e.toString() + " " +e.getCallback().toString()); } // String userName = ((NameCallback)callbacks[0]).getName(); if ( userName== null || userName.length()== 0) { throw new LoginException("User login name is empty!"); } // PasswordCallback passwordCallback= (PasswordCallback)callbacks[1]; char[] password = passwordCallback.getPassword(); passwordCallback.clearPassword(); if ( password== null || password.length== 0) { throw new LoginException("User password is empty!"); } try { // D String dn= this.getUserDN( userName); if ( dn== null) { throw new LoginException("User "+ userName +" doesn‘t exist."); } // E boolean authResult= this.authentication( dn, String.valueOf( password)); if ( authResult== false) { throw new FailedLoginException("User login failed."); } // F principals.add( new WLSUserImpl( userName)); // G List groups= this.getGroupMembership( "ou=groups,o=examples", dn); for ( int i=0, n=groups.size(); i<n; i++) { String cn= String.valueOf(groups.get(i)); // H principals.add( new WLSGroupImpl(cn)); } }catch ( LDAPException ex) { java.io.StringWriter sw = new java.io.StringWriter(); ex.printStackTrace(new java.io.PrintWriter(sw)); sw.flush(); throw new LoginException( sw.toString()); } return loginSucceeded= true; } |
a) 用于保存驗證結果,在后續方法(commit等)中使用
b) 用于保存和用戶(hù)關(guān)聯(lián)的Principal,在后續方法(commit等)中使用
c) 在JAAS中通過(guò)CallbackHandler來(lái)完成用戶(hù)和底層安全認證系統間信息的交換,這里我們通過(guò)CallbackHandler獲取用戶(hù)輸入的登錄賬號和口令。CallbackHandler將在初始化LoginModule時(shí)由WebLogic Security Framework傳入。讀者可能會(huì )問(wèn),我是否可以在此要求WebLogic Security Framework傳入我自己實(shí)現的CallbackHandler,進(jìn)而獲取更多的信息,支持更多的Callback?很遺憾,不可以。只有在一種情況下(本文前面也提到)有一個(gè)變通,就是在Web應用中,通過(guò)Form Based這種認證方式進(jìn)行用戶(hù)身份驗證時(shí),可以獲取更多的登錄表單中提交的cgi變量。
下面的代碼將演示這種技術(shù):
| Callback[] callbacks= new Callback[] { new NameCallback("username: "), new PasswordCallback("password: ",false), new TextInputCallback("my_hidden_field")}; try { callbackHandler.handle( callbacks); }catch (IOException e) { throw new LoginException(e.toString()); }catch (UnsupportedCallbackException e) { throw new LoginException(e.toString() + " " +e.getCallback().toString()); } String value= ((TextInputCallback)callbacks[2]).getText(); |
這樣我們通過(guò)((TextInputCallback)callbacks[2]).getText()來(lái)獲取表單中更多的信息。其中TextInputCallback的Prompt必須要和表單中對應域的名字一致,否則取不到信息。如果有多個(gè)域,則需要傳入多個(gè)TextInputCallback。
同時(shí)如果登錄表單中沒(méi)有這些對應TextInputCallback的域,或者使用JNDI方式驗證,那么會(huì )拋出UnsupportedCallbackException。
下面是一個(gè)符合Servlet安全規范的登錄表單,其中域my_hidden_field的值將通過(guò)CallbackHandler傳遞到的TextInputCallback中:
| <html> <form method=”POST” action=”j_security_check”> <input type=”text” name=”j_username”> <input type=”password” name=”j_password”> <input type=”hidden” name=”my_hidden_field”value=”Hello Callback!”> </form> </html> |
d) 生成一個(gè)用于存放Principal的集合對象
e) 通過(guò)登錄賬號獲取LDAP中的Entry DN
f) 通過(guò)DN和用戶(hù)口令進(jìn)行身份驗證
g) 將通過(guò)驗證的用戶(hù)名加入到Principal列表中,這些以后都將代表用戶(hù)的一定權限
h) 查找用戶(hù)屬于哪些組
i) 將這些組的名字加入到Principal列表中
4.5.6 LoginModule中的commit()和abort()方法實(shí)現
完成了以上的驗證后,我們需要通過(guò)LoginModule.commit()方法提交驗證結果;或者abort()方法取消驗證結果。
| // A private Subject subject; /** * * * LoginException */ public boolean commit() throws LoginException { if (loginSucceeded) { // B subject.getPrincipals().addAll(principals); return true; }else { return false; } } /** * * * LoginException */ public boolean abort() throws LoginException { // C subject.getPrincipals().removeAll(principals); return true; } |
我這里仍然通過(guò)代碼中的注釋進(jìn)行相應的說(shuō)明:
a) JAAS Subject,在LoginModule初始化時(shí),由WebLogic Security Framework負責傳入,用戶(hù)的權限信息(Principals等)都將封裝到該Subject中。同時(shí)Principal Validator會(huì )對Subject中的Principals進(jìn)行簽名,防止被黑客纂改。
b) 用戶(hù)認證成功的情況下,將用戶(hù)關(guān)聯(lián)的Principals加入到Subject中。這樣用戶(hù)在進(jìn)行后續的訪(fǎng)問(wèn)時(shí),WebLogic Security Framework會(huì )檢查與用戶(hù)關(guān)聯(lián)的Subject中是否有可以訪(fǎng)問(wèn)受限資源的Principal。
c) 用戶(hù)登錄失敗的情況下(有可能是其他的LoginModule導致的整體登錄失敗,具體參考JAAS Control Flag),我們在commit中添加的Principals必須從Subject中刪除。
通過(guò)上面的描述和討論,相信讀者已經(jīng)對如何實(shí)現一個(gè)LDAP AuthenticationProvider/LoginModule有了比較清楚的了解。限于篇幅省略了很多細節,對此有需要的讀者可以根據文中給出的相關(guān)鏈接做進(jìn)一步的了解;或者與我交流。從這里我們也可以看到核心的邏輯還是比較簡(jiǎn)單的。其中將LDAP Group作為用戶(hù)的Principal,是J2EE Security與LDAP Security結合的非常重要的一步。通過(guò)這種結合,我們可以將LDAP作為后端,設計出基于J2EE,JAAS的用戶(hù)身份驗證,權限管理等系統。
5 部署中的注意事項
開(kāi)發(fā)完成LDAP Authentication Provider后,需要將通過(guò)WebLogic MBean Maker生成的MBean Jar File放到/server/lib/mbeantypes目錄下。由于WebLogic Server在啟動(dòng)時(shí)會(huì )加載這些Provider,因此在一個(gè)分布式的環(huán)境中,WebLogic Domain內的全部WLServer(包括Managed Server)均需要部署該Jar包。
同時(shí)由于使用了Provider MBean,因此,當你刪除了MBean Types中定義的,并且通過(guò)修改保存在config.xml(WebLogic Domain配置文件)中的屬性時(shí),重新部署的Jar包會(huì )導致WebLogic Server無(wú)法正常啟動(dòng),因為它沒(méi)有辦法按照config.xml中配置的屬性初始化MBean。此時(shí)需要手工修改config.xml,刪除相關(guān)的屬性即可。
<examples.security.providers.authentication.simple.SimpleSampleAuthenticator
ControlFlag="OPTIONAL"
Name="Security:Name=myrealmSimpleSampleAuthenticator"
UserBaseDN="ou=people, o=examples" Realm="Security:Name=myrealm"/>
比如UserBaseDN已經(jīng)不需要通過(guò)MBean來(lái)管理,并且在MBean Types中已經(jīng)刪除,那么直接刪除這里的UserBaseDN="ou=people, o=examples" 就可以了。
6 結束語(yǔ)
本文所討論的內容有些過(guò)于廣泛,從我個(gè)人角度講,寫(xiě)起來(lái)也比較困難,很多技術(shù)問(wèn)題沒(méi)有進(jìn)行深入的討論。其實(shí)只是希望通過(guò)本文,能夠幫助讀者對WebLogic Security,J2EE Security,LDAP Security以及JAAS有一個(gè)初步的認識;能夠給希望實(shí)現LDAP Authentication Provider,或者被WebLogic LDAP Authentication Provider困惑的讀者一些啟示。只要能夠對讀者有所幫助,本文的目的就算達到了。
限于篇幅,本文很多地方的表述可能并不清晰,也可能會(huì )有一些錯誤,如果在閱讀過(guò)程中產(chǎn)生任何疑問(wèn)或者有任何看法都可以通過(guò)E-mail來(lái)與我交流,我也非常希望能和大家交流。
7 參考資料
a) http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777 關(guān)于WebLogic Resource的更多說(shuō)明
b) http://e-docs.bea.com/wls/docs81/security/index.html 關(guān)于WebLogic Security的全部?jì)热?br>c) http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542 關(guān)于Role Mapping Provider的更多內容
d) http://dev2dev.bea.com.cn/techdoc/webser/2005012102.html 關(guān)于WebLogic Console的擴展
e) http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp Custom Seuciryt Provider的Samples
f) http://java.sun.com/products/JavaManagement/wp/ 更多關(guān)于Sun JMX
g) http://java.sun.com/products/jndi/tutorial/ldap/connect/index.html 關(guān)于如何使用Sun JNDI LDAP Service Provider中提供的連接池
h) http://www.ietf.org/rfc/rfc2254.txt 更多關(guān)于LDAP Filter
i) http://www.ietf.org/rfc/rfc2251.txt LDAP V3協(xié)議
j) http://www.ietf.org/rfc/rfc2222.txt 更多關(guān)于SASL

| 作者簡(jiǎn)介 | |
| 馬曉強是高級軟件工程師,現在廣東從事J2EE,LDAP相關(guān)的設計開(kāi)發(fā)工作。對WebLogic,LDAP以及Single Sign-On等產(chǎn)品、技術(shù)很感興趣。 | |

![]() |
![]() |
![]() |

聯(lián)系客服