定制自己的WebLogic LDAP Authentication Provider

時間:2005-04-20
作者:馬曉強(qiáng)
瀏覽次數(shù): 158322
本文關(guān)鍵字:
文章工具
推薦給朋友 推薦給朋友
打印文章 打印文章
目錄

1 J2EE Security 和 LDAP Security
2 JAAS和WebLogic Security Framework
3 了解WebLogic LDAP Authentication Provider
4 定制自己的Custom LDAP Authentication Provider
5 部署中的注意事項(xiàng)
6 結(jié)束語
7 參考資料

  從WebLogic Server 7.0開始,WebLogic Server的安全機(jī)制有了全面的改變,實(shí)現(xiàn)了一個更加規(guī)范的基于JAAS的Security Framework,以及提供了一系列設(shè)計(jì)良好的Security Service Provider Interface。這樣我們可以根據(jù)自己的具體需求,通過Custom Security Authentication Provider來實(shí)現(xiàn)安全上的定制功能。

  本文將以WebLogic(WebLogic Server 8.1) Security和 LDAP為基礎(chǔ),介紹Custom LDAP Authentication Provider如何給我們帶來更多的靈活性,和系統(tǒng)安全設(shè)計(jì)上更多的空間;以及討論如何實(shí)現(xiàn)一個Custom LDAP Authentication Provider和部署過程中的一些良好經(jīng)驗(yàn)。

  由于本文涉及到的范圍太廣,不可能一一詳細(xì)討論;為了使沒有相關(guān)基礎(chǔ)的讀者也能夠閱讀理解本文,因此我將在文章前半部分,試圖通過最簡潔扼要的描述,來使大家對于J2EE Security,WebLogic Security Framework以及LDAP 等有一個初步的清晰認(rèn)識;進(jìn)而可以開發(fā)出自己的LDAP Authentication Provider。因此很多地方做了比較有限的描述或者介紹,更多詳細(xì)的內(nèi)容可以參考文后附帶的參考資料或者文中給出的鏈接。

1 J2EE Security 和 LDAP Security
  Sun J2EE推出以來,其安全部分的規(guī)范就一直倍受關(guān)注。我們最常見到安全規(guī)范的兩個方面分別是Servlet Security 和 EJB Security。目前絕大多數(shù)的Servlet容器,J2EE容器都能很好的支持這些安全規(guī)范。

  WebLogic Server作為業(yè)界領(lǐng)先的J2EE服務(wù)器對J2EE Security的支持是非常優(yōu)秀的。我們這里將結(jié)合WebLogic Security和使用越來越廣泛的LDAP做一個簡要的介紹,這些是設(shè)計(jì)開發(fā)Custom LDAP Authentication Provider的技術(shù)基礎(chǔ)。

1.1 Authentication 和Authorization
  這里需要大家先明確安全上的兩個重要名詞:一個是認(rèn)證(Authentication),一個是授權(quán)(Authorization)。認(rèn)證是回答這個人是誰的問題,即完成用戶名和密碼的匹配校驗(yàn);授權(quán)是回答這個人能做什么的問題。我們討論的J2EE Security包括Declarative Authorization和Programmatic Authorization,即一個是通過web.xml,ejb-jar.xml等部署描述符中的安全聲明通過容器提供的服務(wù)來完成權(quán)限控制的;一個是通過HttpServletRequest.isUserInRole()和EJBContext.isCallerInRole()這樣的編程接口在應(yīng)用中自己完成權(quán)限控制的。

1.2 資源(Resource)和Security Role
  
資源原本只包括 Web Resource和EJB Resource,但在WebLogic Security中擴(kuò)展到幾乎任何一個WebLogic Platform中的資源,具體可以參考http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777。授權(quán)就是針對資源的訪問控制。

  J2EE Security是基于Security Role的。我們可以將一組資源與一個Security Role進(jìn)行關(guān)聯(lián)來達(dá)到控制的目的——只有擁有該Role權(quán)限的用戶才能夠訪問這些資源。簡單的說,我們可以通過給用戶分配不同的Security Role來完成權(quán)限的控制。復(fù)雜的情況下包括用戶/用戶組,以及Principal和Role的映射關(guān)系等等。下面是一個聲明性安全在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的用戶才能夠訪問welcome.jsp頁面,否則容器會返回401無權(quán)訪問的錯誤。更多信息請參考http://e-docs.bea.com/wls/docs81/security/index.html

  同時我們需要在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”的用戶(Principal將封裝到Subject中,用戶將和Subject關(guān)聯(lián))將會擁有PayrollAdmin的權(quán)限。

  注意:一般情況下為了簡化設(shè)計(jì),本文中將假設(shè)security role即是principal name(如果不配置security-role-assignment,WebLogic會默認(rèn)做此假設(shè))。即上例中Principal-name也為PayrollAdmin。

1.3 LDAP Security
  
LDAP是輕量級目錄服務(wù)(Lightweight Directory Access Protocol)。越來越多的應(yīng)用開始采用LDAP作為后端用戶存儲。在安全上,LDAP Security是基于ACL(Access Control List)的,它通過給一個用戶組分配LDAP 操作資源(比如對一個子樹的查詢,修改等)來最終完成權(quán)限的控制。因此在LDAP中,授權(quán)工作是以用戶組為單位進(jìn)行的。一個用戶組一般來說是擁有如下一組屬性的LDAP Entry:


圖1-3-1

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


圖1-3-2

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


圖1-3-3

  這樣將表示Accounting Managers中的成員,同時也是組HR Managers的成員。通過這種層級關(guān)系可以使權(quán)限分配變的更加靈活。

  下面是一些名詞的解釋,希望大家對LDAP有更好的理解:
  a) Objectclass —— LDAP對象類,抽象上的概念類似與一般我們理解的class。根據(jù)不同的objectclass,我們可以判斷這個entry是否屬于某一個類型。比如我們需要找出LDAP中的全部用戶:(objectclass=person)再比如我們需要查詢?nèi)康腖DAP組:(objectclass=groupOfUniqueNames)

  b) Entry —— entry可以被稱為條目,或者節(jié)點(diǎn),是LDAP中一個基本的存儲單元;可以被看作是一個DN和一組屬性的集合。 屬性可以定義為多值或者單值。

  c) DN —— Distinguished Name,LDAP中entry的唯一辨別名,一般有如下的形式:uid=ZHANGSAN, ou=staff, ou=people, o=examples。LDAP中的entry只有DN是由LDAP Server來保證唯一的。

  d) LDAP Search filter ——使用filter對LDAP進(jìn)行搜索。 Filter一般由 (attribute=value) 這樣的單元組成,比如:(&(uid=ZHANGSAN)(objectclass=person)) 表示搜索用戶中,uid為ZHANGSAN的LDAP Entry.再比如:(&(|(uid= ZHANGSAN)(uid=LISI))(objectclass=person)),表示搜索uid為ZHANGSAN, 或者LISI的用戶;也可以使用*來表示任意一個值, 比如(uid=ZHANG*SAN),搜索uid值以 ZHANG開頭SAN結(jié)尾的Entry。更進(jìn)一步,根據(jù)不同的LDAP屬性匹配規(guī)則,可以有如下的Filter: (&(createtimestamp>=20050301000000)(createtimestamp<=20050302000000)),表示搜索創(chuàng)建時間在20050301000000和20050302000000之間的entry。
  Filter中 “&” 表示“與”;“!”表示“非”;“|”表示“或”。根據(jù)不同的匹配規(guī)則,我們可以使用“=”,“~=”,“>=”以及“<=”,更多關(guān)于LDAP Filter讀者可以參考LDAP相關(guān)協(xié)議:http://www.ietf.org/rfc/rfc2254.txt

  e) Base DN —— 執(zhí)行LDAP Search時一般要指定basedn,由于LDAP是樹狀數(shù)據(jù)結(jié)構(gòu),指定basedn后,搜索將從BaseDN開始,我們可以指定Search Scope為:只搜索basedn(base),basedn直接下級(one level),和basedn全部下級(sub tree level)。

  下面是一個典型的LDAP Tree結(jié)構(gòu),右側(cè)顯示Entry uid=ZHANGSAN, ou=staff, ou=people, o=examples的屬性,該entry代表了一個名字叫張三的用戶:


圖1-3-4

2 JAAS和WebLogic Security Framework
  現(xiàn)在越來越多的人開始了解JAAS,使用JAAS。WebLogic Security Framework就是基于JAAS的。因此我們需要對此有一個非常準(zhǔn)確的理解才能夠設(shè)計(jì)開發(fā)Custom Authentication Provider。

  下面我們從幾個名詞入手,了解JAAS和 WebLogic Security Framework的關(guān)鍵之處:

2.1 Principal,Subject和LoginModule
  
a) Principal
  當(dāng)用戶成功驗(yàn)證后,系統(tǒng)將會生成與該用戶關(guān)聯(lián)的各種Principal。我們這里將Principal進(jìn)行簡化的設(shè)計(jì),認(rèn)為一個Principal就是用戶登錄帳號和它所屬于的組(LDAP Group)。這樣當(dāng)用戶登錄成功后,我們將會在LDAP中執(zhí)行搜索,找出用戶屬于哪些組,并將這些組的名字,或者其標(biāo)識作為Principal返回。這樣,當(dāng)用戶在LDAP中屬于某一個組,并且這個組的名字對應(yīng)到 web.xml (或者ejb-jar.xml)中的Security role,那么這個用戶就可以看作擁有訪問這個Security Role定義的資源的權(quán)限。
  在WebLogic Security Framework中,這個LDAP Group的名字(Principal)和Security Role的映射關(guān)系,可以通過一個 Role Mapping Provider來實(shí)現(xiàn)動態(tài)的匹配,即用戶的動態(tài)權(quán)限控制。比如在運(yùn)行時根據(jù)某一個業(yè)務(wù)邏輯來決定用戶是否擁有某一個權(quán)限。關(guān)于Role Mapping Provider,讀者可以參考下面鏈接的內(nèi)容:http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542

  b) Subject
  JAAS規(guī)定由Subject封裝用戶以及用戶認(rèn)證信息,其中包括Principals。下面是WebLogic Security Framework中Subject的組成圖示:


圖2-1-1

  這樣當(dāng)用戶試圖訪問一個受限的J2EE資源時,比如一個web URL,或者一個 EJB Method(可以在web.xml或者ejb-jar.xml中定義,由Security Role控制),WebLogic Security Framework將會通過 Authorization Provider檢查用戶當(dāng)前的Subject中是否包含有是否可以訪問受限資源的Principals。由于Principals將和J2EE Security Role在weblogic.xml中定義一個映射關(guān)系(或者通過其他業(yè)務(wù)邏輯來確定這種關(guān)系),因此通過這樣的關(guān)系,可以最終知道用戶是否有某一個J2EE Resource的訪問權(quán)限。

  c) LoginModule
  JAAS LoginModule是一個Authentication Provider必須的組成部分。LoginModule是認(rèn)證的核心引擎,它負(fù)責(zé)對用戶身份進(jìn)行驗(yàn)證,同時將返回與用戶關(guān)聯(lián)的Principals(用戶登錄帳號,以及LDAP Groups),然后放入Subject中,供后續(xù)的訪問控制使用。
  我們將在LoginModules中完成LDAP的相關(guān)認(rèn)證,查詢操作,將用戶在LDAP中所屬于的組搜索出來,作為認(rèn)證后的結(jié)果封裝到Subject中返回。

2.2 WebLogic Authentication認(rèn)證過程
  下面我們了解一下WebLogic的認(rèn)證過程。以下圖片來自http://e-docs.bea.com/wls/docs81/dvspisec/atn.html 我將其中主要部分進(jìn)行說明。


圖2-2-1

  Security Framework在WebLogic Server啟動時初始化Authentication Provider(5)。當(dāng)有認(rèn)證請求進(jìn)入時,Security Framework首先將通過AuthenticationProvider.getLoginModuleConfiguration()來獲取一個AppConfigurationEntry對象。通過AppConfigurationEntry(詳見http://java.sun.com/security/jaas/apidoc/javax/security/auth/login/AppConfigurationEntry.html )可以初始化一個LoginModule。初始化LoginModule的方法為:public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options),可以看到里面有Subject, CallbackHandler等等重要參數(shù)。
  被實(shí)例化的JAAS LoginModule將完成用戶的一系列驗(yàn)證任務(wù)。驗(yàn)證完成后,在(6a)中principals將被Principal Validation Provider簽名,在( 6b)中存放到與用戶關(guān)聯(lián)的Subject里面。Security Framework最后將拿著該用戶的Subject去完成其他權(quán)限控制等任務(wù)。

3 了解WebLogic LDAP Authentication Provider
  現(xiàn)在我們有信心了解一下WebLogic LDAP Authentication Provider的工作原理了。這里將以WebLogic提供的iPlanet Authentication Provider的配置為例進(jìn)行說明。在這里也需要明確說明一下,為了方便進(jìn)行描述,我們將實(shí)際屬于LoginModule的行為也一并歸結(jié)到Provider中。沒有單獨(dú)將兩個的行為分開,目的是為了突出整個完整的過程。

3.1 iPlanet Authentication Provider配置


圖3-1-1

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


圖3-1-2

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


圖3-1-3

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


圖3-1-4

3.1.9 Static Member DN Attribute —— 靜態(tài)成員屬性,通過該屬性可以判斷一個Entry是否屬于一個組
3.1.10 Static Group DNs From Member DN Filter —— 通過該filter可以找出用戶屬于哪些組
3.1.11 Dynamic Group —— 動態(tài)組是在運(yùn)行時根據(jù)某種業(yè)務(wù)邏輯,來決定成員隸屬關(guān)系的LDAP Group

3.2 iPlanet Authentication Provider的工作原理
  從上面配置的介紹中可以看出,后端存儲為LDAP的情況下,在Console中我們需要配置的參數(shù)已經(jīng)清楚的表明它所要完成工作的內(nèi)容。
  首先,它使用配置的User BaseDN和Filter,來根據(jù)用戶輸入的登錄帳號進(jìn)行搜索,找出存放在LDAP中的用戶Entry。如果找到一個用戶,那么Provider就使用該用戶的DN和用戶輸入的口令,進(jìn)行驗(yàn)證。驗(yàn)證可以使用LDAP Bind和LDAP Compare,這需要根據(jù)不同LDAP的特點(diǎn)來進(jìn)行選擇。

  A. LDAP Bind —— 將當(dāng)前的LDAP Connection綁定到一個用戶身份上。這樣后續(xù)的使用該Connection的LDAP Operation都將以該身份進(jìn)行。LDAP Bind需要兩個重要參數(shù),一個是用戶Entry的DN,一個是該用戶的口令。

  B. LDAP Compare —— LDAP Compare是一個為兼容X.500的古老操作,它用于檢查一個屬性值是否包含在指定Entry中的屬性里。這樣我們可以在知道用戶password存放在哪個屬性的前提下,對該屬性進(jìn)行compare。如果成功,表明口令正確。如果屬性值為散列后的口令(絕大多數(shù)情況),有的LDAP Server支持這樣的驗(yàn)證,有的不支持,比如iPlanet LDAP Server 5。

  驗(yàn)證成功后,Provider將使用Console中關(guān)于Groups和Memberships中的配置,查找用戶屬于哪些LDAP Group,而且由于這些組本身可能會有一些嵌套,因此對于搜索到的組還需要進(jìn)行查詢。即使用filter: (&(uniquemember=uid=ZHANGSAN,ou=staff,ou=people,o=examples)(objectclass=groupOfUniqueNames))從Group Base DN開始搜索,將返回用戶所屬的第一層次Group;然后對于這些返回的組DN,仍然需要使用上面的Filter進(jìn)行搜索(uniquemember值替換為組的DN),找出嵌套關(guān)系,直到查詢完成沒有組嵌套為止(此處需要防止陷入嵌套的循環(huán)中,比如Group A 包含了Group B; Group B又包含了Group A,有的LDAP Server可以自動檢測出,有的需要我們程序來判斷)。

  然后將用戶登錄的帳號,用戶所屬組的名字(屬性CN的值或其他),放入Subject中。最后調(diào)用Principal Validator Provider,對Subject中的principals進(jìn)行簽名,來表明該Subject是這個Provider生成的。這樣防止其他攻擊者偽造Subject以及Principal進(jìn)行欺騙。此處也可以解釋為何在不同的WLS Domain間不能夠傳遞Subject,我們可以通過設(shè)置域信任來完成這種Subject的傳遞。設(shè)置域信任使用的Credential就是簽名用的Key。

  圖3-1-5表明了如何設(shè)置WebLogic Domain Credential,默認(rèn)情況下WebLogic Server會在啟動的時候隨即生成一個Credentials(在WLS6.1時,這個值就是system用戶的口令):

  可以想見如果我們實(shí)現(xiàn)自己的Principal Validator Provider,讓它去一個集中的驗(yàn)證服務(wù)器中對Subject進(jìn)行簽名,或者驗(yàn)證Subject,這樣就可以實(shí)現(xiàn)域信任,進(jìn)而完成Application(EJB Tier)層的SSO。

  通過以上的討論,我們對于實(shí)現(xiàn)自己的LDAP Authentication Provider是不是又增加了一份信心?

4 定制自己的Custom LDAP Authentication Provider

  為何要定制自己的Authentication Provider? 由于WebLogic Server已經(jīng)提供了很多默認(rèn)的Authentication Provider在一般情況下我們確實(shí)沒有必要實(shí)現(xiàn)自己的Provider。但是面對某些針對安全方面的復(fù)雜需求時,WebLogic Server提供的Provider很有可能不滿足這些需求,此時就需要我們定制自己的Provider。

  在這一章的開頭部分中我需要簡要討論關(guān)于WebLogic MBean Types,以及WebLogic Console擴(kuò)展等內(nèi)容,目的在于讓讀者了解到我們通過WebLogic Console可以完成對Custom Security Provider的配置和部署,我將以WebLogic 提供的Sample Security Provider為示例進(jìn)行說明。詳細(xì)的信息可以參考以下的一些資源:

http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106272
http://e-docs.bea.com/wls/docs81/dvspisec/atn.html#1106241

  上面兩個鏈接描述了如何創(chuàng)建MBean Types以及在控制臺上配置Custom Authentication Provider.下面這個鏈接中專門介紹了WebLogic Console的擴(kuò)展:

/techdoc/2005012102.html

  讀者可以從http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp下載WLS提供的Sample Security Provider。

4.1 MBean Types和WebLogic Console
  一般情況下,人們可能更習(xí)慣通過WebLogic Console對Security Provider進(jìn)行配置。這里我將簡要描述這個過程,以及可以到達(dá)的一個效果。限于篇幅就不詳細(xì)討論了。

  從weblogic.management.security.authentication.Authenticator擴(kuò)展MBean Types。 MBean Types是MBean(http://java.sun.com/products/JavaManagement/wp/)的工廠,我們擴(kuò)展SampleSecurityProviders81 包中的SimpleSampleAuthenticator.xml(MBean Definition File),增加一個我們自定義的參數(shù)LDAP Server IP:

<MBeanAttribute
  Name = "LDAPServerIP"
  Type = "java.lang.String"
  Writeable = "true"
  Default = "&quot;127.0.0.1&quot;"
/>

  這樣在Provider中我們將通過MbeanMaker(WLS提供)生成的SimpleSampleAuthenticatorMBean中取到這個屬性:SimpleSampleAuthenticatorMBean.getLDAPServerIP()。MBean將在初始化Provider的時候作為參數(shù)傳入。這樣我們就可以通過MBean中的參數(shù)控制Provider的行為。

  當(dāng)然這個參數(shù)是可以在WebLogic Console中設(shè)置的,通過對MBean Types的擴(kuò)展,在WebLogic Console上看到的畫面如下:


圖4-1-1

  這樣我們可以通過Console修改配置參數(shù)(修改的Security Provider參數(shù)將保存在config.xml中,默認(rèn)的值將保存在MBean Jar File中)。

4.2 為何定制LDAP Authentication Provider
  
當(dāng)我們面臨越來越復(fù)雜的安全方面的業(yè)務(wù)需求時,或者面臨較高的性能要求,需要根據(jù)目標(biāo)LDAP做針對性的優(yōu)化時,或者需要將我們已有的認(rèn)證,或授權(quán)模塊集成到WebLogic平臺時,WebLogic提供的現(xiàn)成的Provider往往不能滿足我們的需求。

4.2.1 復(fù)雜的業(yè)務(wù)需求
  當(dāng)系統(tǒng)要求用戶不僅僅輸入用戶名(j_username),口令(j_password),還需要輸入其他信息,比如登錄的地點(diǎn),系統(tǒng)的名字,用戶的類型等等。如果是采用基于J2EE Form的驗(yàn)證方式, 登錄信息需要提交到j(luò)_security_check(Servlet規(guī)范定義由容器負(fù)責(zé)實(shí)現(xiàn)的Servlet),導(dǎo)致我們沒法處理更多的信息。

  這個時候,如果能夠?qū)崿F(xiàn)我們自己的 Authentication Provider,那么我們就可以通過TextInputCallback來獲取登錄表單中更多的信息了;進(jìn)而通過這些信息在Provider中完成符合我們需要的處理。

  比如搜狐的登錄頁面上需要選擇用戶的類型:


圖4-2-1

4.2.2 性能需求或者調(diào)優(yōu)
  有時,有的用戶會比較困惑為何WebLogic LDAP Security Provider在壓力測試中的表現(xiàn)不很理想,用戶需要較長時間的等待,才能夠登錄到系統(tǒng)中。由于這些Provider是 WebLogic產(chǎn)品的一部分,因此缺乏對不同目標(biāo)LDAP Server的有針對性的優(yōu)化。這樣就使得我們無法充分發(fā)揮具體LDAP Server的性能調(diào)優(yōu)。

  比如,有的LDAP Server支持動態(tài)組(LDAP Dynamic Group,成員關(guān)系是運(yùn)行時根據(jù)ldap server,basedn,filter等動態(tài)決定的),可以使用如下的Filter查詢用戶屬于哪些動態(tài)組:

  (uniquemember=uid=MAXQ,ou=staff,ou=people,o=examples)

  有的LDAP Server雖然支持動態(tài)組,但是支持的有限,不能使用上面的Filter獲取用戶屬于哪些動態(tài)組。在WebLogic iPlanet Authentication Provider的實(shí)現(xiàn)中,它先是搜索出全部的動態(tài)組,然后再遍歷這些動態(tài)組,依次去LDAP中檢查用戶是否屬于一個組;很明顯,這樣雖然最大程度的滿足了不同LDAP Server的要求(從產(chǎn)品的角度講可能是必須的),但是與LDAP交互的次數(shù)大增,并發(fā)用戶量一大性能下降的比較明顯。

  此時,如果系統(tǒng)中的LDAP支持上面的Filter或者有更好的搜索方式,那么完全可以通過定制Provider完成對性能的優(yōu)化。

4.2.3 已有權(quán)限控制的集成
  如果系統(tǒng)中已經(jīng)存在了現(xiàn)成的滿足需求的認(rèn)證模塊,并且已經(jīng)很好的工作;在系統(tǒng)轉(zhuǎn)向J2EE架構(gòu),并使用WebLogic Server做J2EE容器時,我們可能會更愿意直接在Provider中加入這個認(rèn)證模塊。

  綜上,我只是列舉了一些可以驅(qū)動我們開發(fā)自己Provider的需求,相信在讀者實(shí)際工作中可能會面臨更復(fù)雜的情況,開發(fā)自己的Provider將是一個非常好的選擇。

4.3 LDAP Authentication Provider實(shí)現(xiàn)
  本文之前為了表述的方便沒有單獨(dú)提到LoginModule,認(rèn)為LoginModule的行為就是LDAP Authentication Provider的行為。到了目前的具體實(shí)現(xiàn)階段,我們必須分開Authentication Provider和JAAS LoginModule。最終部署到WebLogic上的實(shí)際只是LDAP AuthenticationProvider Implements。

  WebLogic SecurityFramework通過Authentication Provider獲取具體的JAAS LoginModule。通過LoginModule完成最終登錄的工作。因此我們必須先實(shí)現(xiàn)一個AuthenticationProvider。

  我們一般通過weblogic.security.spi.AuthenticationProvider 來實(shí)現(xiàn)自己的AuthenticationProvider。這里介紹其中的幾個重要方法:

  a) public void initialize(ProviderMBean mbean, SecurityServices services)
初始化一個Provider。通過參數(shù)MBean我們可以獲取到在WebLogic Console中配置的各項(xiàng)參數(shù)。進(jìn)而初始化我們的Provider,然后通過Provider傳遞到LoginModule中。

  b) public void shutdown()
釋放一些與Provider,LoginModule等相關(guān)的資源。

  c) public AppConfigurationEntry getLoginModuleConfiguration()
這個方法非常重要,通過該方法,WebLogic Security Framework可以獲取用于初始化LoginModule的AppConfigurationEntry。AppConfigurationEntry中存放了LoginModule的類名等信息,比如使用如下代碼返回一個AppConfigurationEntry:

  return new AppConfigurationEntry(
    "examples.security.providers.authentication.SampleLoginModuleImpl",
    controlFlag,
  options);

  其中LoginModule Name就

是"examples.security.providers.authentication.SampleLoginModuleImpl",我們通過它就可以實(shí)例化一個LoginModule并通過LoginModule.initialize()方法進(jìn)行初始化。

  d) public AppConfigurationEntry getAssertionModuleConfiguration()
該方法將返回一個與Identity Assertion Provider關(guān)聯(lián)的LoginModule。這個Assertion LoginModule,將只會驗(yàn)證用戶是否存在,以及如果存在返回用戶的Principals。 該方法也比較重要,需要正確實(shí)現(xiàn),比如我們使用CLIENT-CERT這種WEB認(rèn)證方式,該方法就會被調(diào)用。

  Provider的實(shí)現(xiàn)比較簡單,讀者可以在http://dev2dev.bea.com/codelibrary/code/security_prov81.jsp下載WebLogic提供的Samples,查看SampleAuthenticationProviderImpl的代碼。

 

4.4 LDAP LoginModule 邏輯流程
  實(shí)現(xiàn)了Provider后,必須擁有我們自己的LDAP LoginModule。下面是一個簡單的用于演示的驗(yàn)證邏輯流程圖。實(shí)際的一個LoginModule由于不同的業(yè)務(wù)需求,情況可能會復(fù)雜得多。這里只是描述了最核心最基本的邏輯,使讀者能有一個清晰的思路。后面我將以這個流程為例進(jìn)行實(shí)現(xiàn)。

4.5 LoginModule代碼示例和講解
  這里我將使用Netscape LDAP SDK for java作為開發(fā)工具實(shí)現(xiàn)LDAP相關(guān)的操作,讀者可以到http://docs.sun.com/db/doc/816-6402-10 下載開發(fā)手冊,從http://www.mozilla.org/directory/ 下載SDK 包。一般來說還可以通過JNDI來操作LDAP,我個人認(rèn)為Sun LDAP JNDI Provider中關(guān)于Connection Pool的實(shí)現(xiàn)非常優(yōu)秀。但不管使用哪種SDK,對LDAP的編程原理上基本都是相同的(因?yàn)榛谕瑯拥腖DAP協(xié)議),不同的可能僅僅是接口,類的名字而已。

4.5.1 初始化Connection Pool
  為了有效使用寶貴,并且有限的LDAP連接,必須使用連接池。下面的代碼初始化了一個LDAP連接池:

/**
*
*/
static ConnectionPool pool;
/** * * @throw 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)中可以通過在環(huán)境變量中設(shè)置具體的參數(shù)來啟用連接池,達(dá)到復(fù)用連接的目的。具體可以參考鏈接: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 根據(jù)用戶輸入的登錄帳號,搜索用戶Entry
  下面這個方法實(shí)現(xiàn)了從LDAP中搜索用戶Entry DN的最簡單的過程。實(shí)際上我們可以在其中實(shí)現(xiàn)很多定制的功能。比如允許用戶使用多于一個的帳號登錄,只要這些帳號能夠通過LDAP Searche最終返回一個唯一的用戶DN即可。

  * @return
   * @throws 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)
{}
    }
  }

  首先需要從池中獲取一個LDAP連接,然后使用LDAPConnection.search方法進(jìn)行搜索。我這里以一個典型的LDAP Search接口為例進(jìn)行說明,其他API比如JNDI等,基于同樣的LDAP協(xié)議,接口中同樣參數(shù)的含義都是相同的。

public LDAPSearchResults search(java.lang.String base,
  int scope,
  java.lang.String filter,
  java.lang.String[] attrs,
  boolean attrsOnly)

  1) Base: 表明從該Basedn開始搜索,可以通過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:過慮條件。比如uid=ZHANGSAN,搜索UID為ZHANGSAN的用戶;再比如uid=ZHANG*,搜索 ZHANG開頭的帳號;或者uid=Z*S,搜索以Z開頭S結(jié)尾的帳號。前面已經(jīng)介紹過這里就不多說了。

  4) attrs:返回該attrs數(shù)組中指定的屬性,比如new String[]{“uid”},只返回屬性uid,其他屬性將會不在結(jié)果中返回。 一般來說我們會要求開發(fā)人員只將需要的屬性返回,這樣避免返回?zé)o用的屬性,降低網(wǎng)絡(luò)和Server等方面的資源開銷;而且如果存在一個有較大屬性集合的Entry,并且你并不使用到這個較大的屬性集合。舉個實(shí)際例子來說,比如你的系統(tǒng)中擁有很多成員數(shù)目超過2萬或者更多的LDAP Groups,并且你希望通過LDAP Search找出某一個用戶屬于的組CN,那么搜索結(jié)果只返回組的CN已經(jīng)可以滿足你的要求了,這時就沒有必要將全部member屬性也返回了。這在后面我會有代碼來說明。

  這里還有一點(diǎn)很重要的細(xì)節(jié),相信對讀者會有幫助。比如,你希望搜索到modifytimestamp等Operational Attributes。這些屬性(LDAP Server自己負(fù)責(zé)維護(hù)的,用戶無法修改的)必須要在參數(shù)attrs中指定,LDAP Server才會返回給客戶端。如果用戶希望返回全部User Attributes的同時,返回指定的 Operational Attributes那該怎么辦?不在attrs列表中的屬性將不會被返回,一旦我指定了attrs,是否需要將全部的屬性都列在attrs參數(shù)中?實(shí)際上此時只需要傳入 new String[]{ “*”, “createtimestamp”}這樣的參數(shù)即可;“*”號即代表了全部的User Attributes。在Sun JNDI LDAP Service Provider中也是這樣,盡管找遍Sun關(guān)于JNDI的文檔也找不到這樣的說明。LDAP協(xié)議對此的說明在 http://www.ietf.org/rfc/rfc2251.txt 第28頁。

  5) attrsOnly:只返回屬性名稱,不包含值。我們一般設(shè)置為false。

  我們下面看一下Sun JNDI中關(guān)于LDAP Search的接口:

public NamingEnumeration search(String name,
  String filter,
  SearchControls cons)
  throws NamingException
name參數(shù)就是上面的basedn,SearchControls中封裝更多的設(shè)置,比如:SearchControls. setSearchScope(int scope)其中的scope對應(yīng)上面的2); SearchControls.setReturningAttributes(String[] attrs),設(shè)置指定返回的屬性,對應(yīng)上面的4)。

  其他的參數(shù)還包括Size Limit,即限制搜索結(jié)果的數(shù)目(LDAP返回的Entry一般情況下是沒有排序的,除非使用一些Sort Control),當(dāng)你的搜索可能會返回成千上萬的Entry時,限制搜索的數(shù)目是非常明智也是必須的。關(guān)于這些,讀者可以參考API文檔或者LDAP協(xié)議。

4.5.3 根據(jù)用戶的Entry DN,和用戶輸入的口令完成身份驗(yàn)證
  
下面的方法實(shí)現(xiàn)了到LDAP的身份驗(yàn)證。LDAP中的用戶實(shí)際上就是一個LDAP Entry,一次驗(yàn)證實(shí)際是將Connection與這個Entry進(jìn)行綁定,因此驗(yàn)證時我們需要兩個必須的參數(shù),一個是Entry的DN,一個是口令。LDAP的身份驗(yàn)證方式有多種,包括SASL以及基于證書的驗(yàn)證等,我們這里只介紹Simple Authentication。關(guān)于SASL更多信息讀者可以參考:http://www.ietf.org/rfc/rfc2222.txt

  /**
   *
   * @param dn
   * @param pwd
   * @return
   * @throws 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; // 用戶口令錯誤 }
      if ( ex.getLDAPResultCode()== LDAPException.NO_SUCH_OBJECT) {
        return false; // 用戶不存在 }
      throw ex;
    }finally {
      try {
        if ( conn!= null) pool.close(conn);
      }catch ( Exception ex) {
      }
    }
  }


  我們這里使用 LDAP Bind操作完成對用戶的身份驗(yàn)證。本文前面曾提到也可以使用LDAP Compare,通過比較口令屬性(userpassword)中的值來完成驗(yàn)證。我們需要根據(jù)不同的LDAP Server選擇合適的驗(yàn)證方法。

  比如iPlanet LDAP Server 5,只能通過Bind完成認(rèn)證;而Oracle Internet Directory可以通過LDAP Compare完成驗(yàn)證,而且還支持口令策略。

  如果我們使用了連接池,連接池中的連接一般都是使用權(quán)限較大的用戶初始化的,這樣這些連接才可以完成對LDAP的搜索操作;而當(dāng)通過這些連接對普通用戶進(jìn)行身份驗(yàn)證時,如果通過驗(yàn)證,連接的身份將被改變?yōu)槠胀ǖ挠脩簦ɑ蚍Q為與普通用戶的身份關(guān)聯(lián))。普通用戶很可能沒有除了bind以外的任何權(quán)限,所以在連接被放入池中前,我們必須要恢復(fù)連接的身份。

  這樣我們必須執(zhí)行兩次LDAP Bind,一次用于對普通用戶驗(yàn)證身份;一次用于恢復(fù)連接的較大權(quán)限的用戶身份。我們看到這樣效率是比較低的,可能你在LDAP Server端統(tǒng)計(jì)有2萬次bind請求,實(shí)際上只有1萬人次登錄。

  對于特定的LDAP Server,比如 Oracle Internet Directory,可以通過LDAP Compare對用戶身份進(jìn)行驗(yàn)證,并且不會改變連接關(guān)聯(lián)的用戶身份。這樣我在使用池的情況下只需要一次LDAP Compare即可。效率有很大提高。如果不通過定制LDAP Authentication Provider,這樣的調(diào)優(yōu)是沒法實(shí)現(xiàn)的。

  Netscape LDAP SDK的ConnectionPool的實(shí)現(xiàn)中,在連接放入池中前會檢查連接的身份,如果身份被改變,那么會重新進(jìn)行bind。所以我們沒有必要在代碼中再做bind。

4.5.4 根據(jù)用戶的DN搜索用戶屬于的組列表
  
有了上面的基礎(chǔ)后,這個方法就很容易理解了。下面我們來看看如何返回用戶所屬的組。

 

/**
   *
   * @param groupbasedn
   * @param memberDn
   * @return
   * @throws 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主要是通過uniquemember或者 member屬性來定義的。成員不僅僅可以使用用戶,也可以是組,因?yàn)榻M可以嵌套。

  a) 上面的方法中只實(shí)現(xiàn)了一個層次的搜索,即用戶——組的搜索,而組間的嵌套搜索沒有實(shí)現(xiàn)。讀者可以根據(jù)系統(tǒng)內(nèi)的具體情況,在此處也可以做一些優(yōu)化。

  b) 組成員的數(shù)量可能比較大,為了避免不必要的開銷,我們指定只返回組的cn屬性。

  c) 動態(tài)組的優(yōu)化。在WebLogic默認(rèn)的實(shí)現(xiàn)中,Provider(實(shí)際為LoginModule)會不斷的拿動態(tài)組中定義的URL中的filter和用戶的DN去LDAP做搜索,來看用戶是否屬于該組。本文前面也討論了,這樣效率很低,完全可以放在Provider本地實(shí)現(xiàn)URL和Entry的匹配。

4.5.5 LoginModule中的login()方法實(shí)現(xiàn)
  
一切準(zhǔn)備就緒后,我們就可以完成LoginModule.login()這個最核心的方法了。下面我根據(jù)代碼中的注釋逐條說明。

  // A
  boolean loginSucceeded;
  // B
  List principals= new java.util.ArrayList();
  /**
   *
   * @return
   * @throws 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) 用于保存驗(yàn)證結(jié)果,在后續(xù)方法(commit等)中使用

  b) 用于保存和用戶關(guān)聯(lián)的Principal,在后續(xù)方法(commit等)中使用

  c) 在JAAS中通過CallbackHandler來完成用戶和底層安全認(rèn)證系統(tǒng)間信息的交換,這里我們通過CallbackHandler獲取用戶輸入的登錄帳號和口令。CallbackHandler將在初始化LoginModule時由WebLogic Security Framework傳入。讀者可能會問,我是否可以在此要求WebLogic Security Framework傳入我自己實(shí)現(xiàn)的CallbackHandler,進(jìn)而獲取更多的信息,支持更多的Callback?很遺憾,不可以。只有在一種情況下(本文前面也提到)有一個變通,就是在Web應(yīng)用中,通過Form Based這種認(rèn)證方式進(jìn)行用戶身份驗(yàn)證時,可以獲取更多的登錄表單中提交的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();

  這樣我們通過((TextInputCallback)callbacks[2]).getText()來獲取表單中更多的信息。其中TextInputCallback的Prompt必須要和表單中對應(yīng)域的名字一致,否則取不到信息。如果有多個域,則需要傳入多個TextInputCallback。

  同時如果登錄表單中沒有這些對應(yīng)TextInputCallback的域,或者使用JNDI方式驗(yàn)證,那么會拋出UnsupportedCallbackException。

  下面是一個符合Servlet安全規(guī)范的登錄表單,其中域my_hidden_field的值將通過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) 生成一個用于存放Principal的集合對象
  e) 通過登錄帳號獲取LDAP中的Entry DN
  f) 通過DN和用戶口令進(jìn)行身份驗(yàn)證
  g) 將通過驗(yàn)證的用戶名加入到Principal列表中,這些以后都將代表用戶的一定權(quán)限
  h) 查找用戶屬于哪些組
  i) 將這些組的名字加入到Principal列表中

4.5.6 LoginModule中的commit()和abort()方法實(shí)現(xiàn)
  
完成了以上的驗(yàn)證后,我們需要通過LoginModule.commit()方法提交驗(yàn)證結(jié)果;或者abort()方法取消驗(yàn)證結(jié)果。

  // A
  private Subject subject;
    /**
   *
   * @return
   * @throws LoginException
   */
  public boolean commit() throws LoginException { if (loginSucceeded) {
  // B
      subject.getPrincipals().addAll(principals);
      return true;
    }else {
      return false;
    }
  }
    /**
   *
   * @return
   * @throws LoginException
   */
    public boolean abort() throws LoginException {
    // C
    subject.getPrincipals().removeAll(principals);
    return true;
  }    

 

  我這里仍然通過代碼中的注釋進(jìn)行相應(yīng)的說明:

  a) JAAS Subject,在LoginModule初始化時,由WebLogic Security Framework負(fù)責(zé)傳入,用戶的權(quán)限信息(Principals等)都將封裝到該Subject中。同時Principal Validator會對Subject中的Principals進(jìn)行簽名,防止被黑客纂改。

  b) 用戶認(rèn)證成功的情況下,將用戶關(guān)聯(lián)的Principals加入到Subject中。這樣用戶在進(jìn)行后續(xù)的訪問時,WebLogic Security Framework會檢查與用戶關(guān)聯(lián)的Subject中是否有可以訪問受限資源的Principal。

  c) 用戶登錄失敗的情況下(有可能是其他的LoginModule導(dǎo)致的整體登錄失敗,具體參考JAAS Control Flag),我們在commit中添加的Principals必須從Subject中刪除。


  通過上面的描述和討論,相信讀者已經(jīng)對如何實(shí)現(xiàn)一個LDAP AuthenticationProvider/LoginModule有了比較清楚的了解。限于篇幅省略了很多細(xì)節(jié),對此有需要的讀者可以根據(jù)文中給出的相關(guān)鏈接做進(jìn)一步的了解;或者與我交流。從這里我們也可以看到核心的邏輯還是比較簡單的。其中將LDAP Group作為用戶的Principal,是J2EE Security與LDAP Security結(jié)合的非常重要的一步。通過這種結(jié)合,我們可以將LDAP作為后端,設(shè)計(jì)出基于J2EE,JAAS的用戶身份驗(yàn)證,權(quán)限管理等系統(tǒng)。

5 部署中的注意事項(xiàng)
  開發(fā)完成LDAP Authentication Provider后,需要將通過WebLogic MBean Maker生成的MBean Jar File放到${WL_HOME}/server/lib/mbeantypes目錄下。由于WebLogic Server在啟動時會加載這些Provider,因此在一個分布式的環(huán)境中,WebLogic Domain內(nèi)的全部WLServer(包括Managed Server)均需要部署該Jar包。

  同時由于使用了Provider MBean,因此,當(dāng)你刪除了MBean Types中定義的,并且通過修改保存在config.xml(WebLogic Domain配置文件)中的屬性時,重新部署的Jar包會導(dǎo)致WebLogic Server無法正常啟動,因?yàn)樗鼪]有辦法按照config.xml中配置的屬性初始化MBean。此時需要手工修改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)不需要通過MBean來管理,并且在MBean Types中已經(jīng)刪除,那么直接刪除這里的UserBaseDN="ou=people, o=examples" 就可以了。

6 結(jié)束語
  本文所討論的內(nèi)容有些過于廣泛,從我個人角度講,寫起來也比較困難,很多技術(shù)問題沒有進(jìn)行深入的討論。其實(shí)只是希望通過本文,能夠幫助讀者對WebLogic Security,J2EE Security,LDAP Security以及JAAS有一個初步的認(rèn)識;能夠給希望實(shí)現(xiàn)LDAP Authentication Provider,或者被WebLogic LDAP Authentication Provider困惑的讀者一些啟示。只要能夠?qū)ψx者有所幫助,本文的目的就算達(dá)到了。

  限于篇幅,本文很多地方的表述可能并不清晰,也可能會有一些錯誤,如果在閱讀過程中產(chǎn)生任何疑問或者有任何看法都可以通過E-mail來與我交流,我也非常希望能和大家交流。

7 參考資料
a) http://e-docs.bea.com/wls/docs81/secwlres/types.html#1213777 關(guān)于WebLogic Resource的更多說明
b) http://e-docs.bea.com/wls/docs81/security/index.html 關(guān)于WebLogic Security的全部內(nèi)容
c) http://e-docs.bea.com/wls/docs81/dvspisec/rm.html#1145542 關(guān)于Role Mapping Provider的更多內(nèi)容
d) http://dev2dev.bea.com.cn/techdoc/2005012102.html 關(guān)于WebLogic Console的擴(kuò)展
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