目錄
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./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./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./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./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./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./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./wls/docs81/dvspisec/atn.html#1106272 http://e-docs./wls/docs81/dvspisec/atn.html#1106241
上面兩個鏈接描述了如何創(chuàng)建MBean Types以及在控制臺上配置Custom Authentication Provider.下面這個鏈接中專門介紹了WebLogic Console的擴(kuò)展:
http://dev2dev./techdoc/webser/2005012102.html
讀者可以從http://dev2dev./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./products/JavaManagement/wp/)的工廠,我們擴(kuò)展SampleSecurityProviders81 包中的SimpleSampleAuthenticator.xml(MBean Definition File),增加一個我們自定義的參數(shù)LDAP Server IP:
<MBeanAttribute Name = "LDAPServerIP" Type = "java.lang.String" Writeable = "true" Default = ""127.0.0.1"" /> |
這樣在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./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./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; /** * * 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./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即可。
* * 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./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./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; // 用戶口令錯誤 } 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ǔ)后,這個方法就很容易理解了。下面我們來看看如何返回用戶所屬的組。
/** * * 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主要是通過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(); /** * * * 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; /** * * * 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; } |
我這里仍然通過代碼中的注釋進(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放到/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./wls/docs81/secwlres/types.html#1213777 關(guān)于WebLogic Resource的更多說明 b) http://e-docs./wls/docs81/security/index.html 關(guān)于WebLogic Security的全部內(nèi)容 c) http://e-docs./wls/docs81/dvspisec/rm.html#1145542 關(guān)于Role Mapping Provider的更多內(nèi)容 d) http://dev2dev./techdoc/webser/2005012102.html 關(guān)于WebLogic Console的擴(kuò)展 e) http://dev2dev./codelibrary/code/security_prov81.jsp Custom Seuciryt Provider的Samples f) http://java./products/JavaManagement/wp/ 更多關(guān)于Sun JMX g) http://java./products/jndi/tutorial/ldap/connect/index.html 關(guān)于如何使用Sun JNDI LDAP Service Provider中提供的連接池 h) http://www./rfc/rfc2254.txt 更多關(guān)于LDAP Filter i) http://www./rfc/rfc2251.txt LDAP V3協(xié)議 j) http://www./rfc/rfc2222.txt 更多關(guān)于SASL
作者簡介 |
|
馬曉強(qiáng)是高級軟件工程師,現(xiàn)在廣東從事J2EE,LDAP相關(guān)的設(shè)計(jì)開發(fā)工作。對WebLogic,LDAP以及Single Sign-On等產(chǎn)品、技術(shù)很感興趣。 |
作者其它文章
|