1. 程式人生 > >WebSphere 應用伺服器 V6.1 中的 LDAP 身份認證

WebSphere 應用伺服器 V6.1 中的 LDAP 身份認證

企業資訊化建設多年,建立了包括人事、財務、市場、銷售、客戶關係管理等一系列的業務和應用資訊系統。這些系統種類繁多並且由不同的應用開發商提供,比如 SAP、金碟的 ERP 軟體,微軟、ORACLE、用友的財務軟體等等。不同的系統和開發商有自己專用的一套使用者儲存和管理系統 ( 儲存使用者名稱口令和組等資訊 -- username, password, group etc),例如早期的各種 Unix、Windows 作業系統帳號、後來的各種關係型資料庫、以及目錄伺服器等等。混亂的使用者儲存和管理系統給企業 IT 管理帶來了繁重的維護工作量和新的問題,企業很希望資訊系統能採用標準的使用者登錄檔,輕量級目錄訪問協議 LDAP 由於標準化是一種比較理想的選擇。

有關 IBM WebShpere 應用伺服器安全性、如何配置 LDAP 的文章已經很多,但是大多數是 6.0 以前的版本。在 6.0 以前,WebSphere 只能配置一種使用者登錄檔,比如作業系統、目錄伺服器、定製使用者登錄檔。

本文介紹 WebSphere 6.1 的新特性“聯合儲存庫”,如何為聯合儲存庫配置 LDAP 伺服器,以及如何遷移 J2EE Web 應用程式以使用 J2EE 規範的表單認證,進而能通過 WebSphere 6.x 的儲存庫 ( 聯合儲存庫、其他支援的使用者登錄檔 ) 進行身份認證和授權。

我們會首先來了解一下 WebSphere 6.1 的最新特性“聯合儲存庫”,看看它和 6.0 使用者登錄檔有什麼區別,它是如何同時使用多種儲存庫進行身份認證的; 接下來,我們來看看如何配置“聯合儲存庫”,讓它能夠使用 LDAP 外部儲存庫進行身份認證;配置 LDAP 身份認證是一個需要多方面知識和經驗的工作,我們需要掌握在 WebSphere 配置 LDAP 的過程中如何進行排錯 (trouble shooting) 的技能。配置好了 WebSphere“聯合儲存庫”,WebSphere 預設的例子應用、伺服器管理控制檯已經可以通過“聯合儲存庫”進行身份認證,但是我們現有的部署在 WebSphere 上面的業務應用系統並不能馬上自動地使用到這一特性。讓我們分析一下現有的 J2EE 應用 -- 以 Struts 例子應用為代表,分析一下現有應用常用的認證機制; 接下來我們會設定一下移植現有 J2EE 應用到使用 J2EE 表單登入認證和“聯合儲存庫”的移植目標;根據這個目標,我們對 Struts 例子應用進行移植,為它增加標準的 J2EE 表單登入方式,增加新的 J2EE 標準格式的 Form 登入頁面,最後為 Struts 應用增加新的程式碼,在使用者通過新的 J2EE 標準格式的 Form 登入頁面登入以後,後臺應用系統如何為登入使用者構造使用者會話資訊。

首先,我們來了解一下 WebSphere 6.1 的最新特性“聯合儲存庫”,看看它和 6.0 使用者登錄檔有什麼區別,它是如何同時使用多種儲存庫進行身份認證的。

IBM WAS 可以藉助管理員配置的使用者帳號儲存庫,幫助執行在 WAS 上面的 J2EE 應用程式以標準的 J2EE 認證方式進行使用者身份認證。換言之,J2EE 應用程式可以使用 WAS 上面配置的使用者帳號儲存庫進行統一的使用者身份認證。

WAS V6.0 提供了本地作業系統、獨立 LDAP 登錄檔、獨立定製登錄檔等三種使用者帳號儲存庫。通常 WAS V6.0 管理員只能選擇其中的一種。

WAS V6.1 提供了新的使用者帳號儲存庫—聯合儲存庫,該儲存庫支援同時使用多種儲存庫進行身份認證,包括 IBM 提供的內置於系統的基於檔案的儲存庫、一個或多個 JDBC 關係資料庫儲存庫 (IBM 提供資料庫表的定義和實現 )、一個或多個外部 LDAP 儲存庫。


圖 1:WAS V6.1 的“使用者帳號儲存庫”

下面讓我們來看看如何配置“聯合儲存庫”,讓它能夠使用 LDAP 外部儲存庫進行身份認證。

如果只使用 LDAP 登錄檔作為唯一的使用者帳號儲存庫,那麼可以通過 WAS V6.1 的“安全配置嚮導”即可。其配置方法、介面和 WAS V6.0 基本一樣。


圖 2:WAS V6.1 的“安全配置嚮導”

WAS V6.1“聯合儲存庫”為管理員提供了新的選擇,管理員可以在“聯合儲存庫”中新增一個或多個 LDAP 外部儲存庫 (“聯合儲存庫”可以是由多個 LDAP 外部儲存庫和檔案儲存庫、關係資料庫儲存庫組成,但是這些資料庫裡面不能有重名使用者 )。新增以後,我們不僅可以用 LDAP 進行應用的身份驗證,而且可以利用到 WAS V6.1 聯合儲存庫為使用者和組管理提供的讀寫能力,包括使用以下選項新增、建立和刪除使用者和組:

  • 使用者 / 組管理應用程式程式設計介面 (IBM WebSphere Java API)
  • 管理控制檯
  • wsadmin 命令

注:在 WAS 以前的版本中,LDAP 只能用於身份認證,不能通過 WAS 讀寫 LDAP。

在 WAS V6.1 管理控制檯中選擇導航安全性 > 安全管理、應用程式和基礎結構 > 聯合儲存庫 > 管理儲存庫,選擇按鈕“新增”新的外部儲存庫。

注:在安裝 WAS V6.1 的時候,如果選擇了使能安全性保護 WAS 管理控制檯,那麼你會看到聯合儲存庫裡面已經有了基於檔案的儲存庫。


圖 3:管理儲存庫 

在接下來的“新建”頁面中錄入如下資料:

  • 資料庫標識:選取合適的容易說明即可,可以是伺服器主機名稱、英文說明,都可以。
  • 目錄型別:選擇 IBM Tivoli Directory Server 6,也可以是 Domino、微軟活動目錄等等。
  • 主要主機名:主目錄伺服器主機名。
  • 故障轉移主機名:當主目錄伺服器出故障 out of service 的時候,WAS V6.1 會自動切換使用備份的目錄伺服器。
  • 登入屬性:預設是 uid,如果你想讓使用者能夠以其他 LDAP 屬性登入,比如使用者可以用 uid 或者 cn 或者 email 都能登入,那麼你可以錄入 uid;cn;email。
  • 支援到其它 LDAP 伺服器的指向資訊:LDAP 伺服器的資料可以通過 referral 指向其他 LDAP 伺服器。
  • 需要 SSL 通訊:配置 WAS 和 LDAP 伺服器之間的通訊要走 SSL。
  • 證書對映:如果 J2EE Web 應用使能 SSL 證書登入,那麼 WAS 預設會將客戶瀏覽器提供的 SSL 證書中的 Subject 提取出來,在 LDAP 伺服器中和 inetOrgPerson 類物件的 DN 屬性進行比對,如果相同,那麼這個人的證書認證就算通過了,使用者的身份就是其個人物件 DN 屬性和 SSL 證書 Subject 相同的使用者。

圖 4:新建 LDAP 儲存庫 

新建了 LDAP 外部儲存庫以後,這個庫還沒有真正被“聯合儲存庫”引用,仍需要做一個簡單的引用管理配置。選擇安全性 > 安全管理、應用程式和基礎結構 > 聯合儲存庫,選擇按鈕“將基本條目新增至域”。


圖 5:儲存庫引用 

“將基本條目新增至域”以後,“聯合儲存庫”現在有兩個儲存庫了。使用者登入 J2EE Web 應用的時候,WAS 會先後在兩個儲存庫中進行查詢使用者進行身份認證。


圖 6:儲存有多個儲存庫的聯合儲存庫 

配置完畢“聯合儲存庫”以後,還需要分別“啟用管理安全性”和“啟用應用程式安全性”。

注:在 WebSphere Application Server V6.0.x 的環境中,其 Web 管理控制檯預設是不受保護的,任何人不需提供使用者名稱和口令就能登陸 WAS 的管理控制檯,進行所有的管理操作。

和 6.0 版本不同的是,WAS V6.1 新增了“管理安全性”安裝選項,安裝 WAS 的時候就可以指定是否保護 WAS 的管理伺服器並指定管理員使用者名稱、口令,讓管理員可以輕鬆地保護 WAS 的管理伺服器。WAS V6.1 安裝時候啟用的安全性預設採用“內建儲存庫”,儲存庫標識 InternalFileRepository,儲存庫型別為檔案 ( 儲存位置 WebSphere\AppServer\profiles\AppSrv01\config\cells\<node name>\fileRegistry.xml)。


圖 7:啟用“管理安全性”和“應用程式安全性”

啟用安全性以後停止並重啟 WAS,使用瀏覽器訪問 WAS 6.1 例子應用 DefaultApplication 的 Snoop Servlet,可以 LDAP 的使用者進行登入訪問了。瀏覽器訪問 Snoop URL: http://wp6x.cn.ibm.com:9080/snoop,根據提示輸入 LDAP 使用者名稱口令,訪問成功 !

使用 WAS 的身份驗證帶來的額外好處是,預設情況下單伺服器上面的 Web 應用之間已經實現了單點登入!比如,首先瀏覽器訪問 Snoop URL: http://wp6x.cn.ibm.com:9080/snoop,根據提示輸入 WAS 管理員口令,訪問成功。然後在瀏覽器裡面輸入 WAS V6.1 管理控制檯 URL: http://wp6x.cn.ibm.com:9060/ibm/console/,你會發現不需要再次輸入使用者資訊直接能夠訪問了 ! 如果想做到多伺服器的單點登入,還需要進行一些額外的 WAS LTPA 配置,這裡就不多說了,請參考後面的參考資料。

配置 LDAP 身份認證是一個需要多方面知識和經驗的工作,我們需要掌握在 WebSphere 配置 LDAP 的過程中如何進行排錯 (trouble shooting) 的技能。

WAS LDAP 配置排錯可以從兩方面著手。首先是從 WAS 入手,除了 WAS 標準系統輸出和錯誤檔案 SystemOut.log 和 SystemErr.log 檔案之外,還開啟 WAS 的安全方面的日誌,裡面記錄了從使用者登入、WAS 到 LDAP 伺服器上面進行身份校驗、使用者提交會話 Cookie/LTPAToken 等等資訊。開啟 WAS 日誌的方法是登入 WAS 管理控制檯,導航到記錄和跟蹤 > server1 > 診斷跟蹤服務 > 更高日誌詳細資訊級別,新增有關安全跟蹤的內容 *=info:com.ibm.ws.security.*=all ,儲存配置,重新啟動 WAS 伺服器,進行訪問測試,出問題後檢視位於 <server root>\log 目錄下面的 trace.log 檔案,比如 WebSphere\AppServer\profiles\<node name>\logs\server1\trace.log。


圖 8:啟用安全日誌 

另外,你也可以開啟 LDAP 伺服器的日誌跟蹤,看看 WAS 伺服器那邊發過來的 LDAP 請求到底是什麼內容,特別是 LDAP 繫結、LDAP 搜尋過濾器是否正確,LDAP 返回給 WAS 的結果是否正常。

Domino LDAP 伺服器的日誌開啟方法是,在 Domino 伺服器控制檯上面執行命令 set config LDAPDEBUG=7,結果是在 Domino 伺服器的配置檔案 notes.ini 最後面自動加入了一段 LDAPDEBUG=7,重新啟動 Domino 伺服器或者單獨起停 LDAP 伺服器。當 WAS 以 LDAP 協議訪問 Domino 的時候,所有 LDAP 的訪問請求互動資訊將列印到 Domino 伺服器控制檯上面。

配置了 WAS 伺服器“聯合儲存庫”使用 LDAP 伺服器進行身份認證,我們看到了 WAS 內建的例子應用 DefaultApplication 受到安全保護,WebSphere 預設的例子應用、伺服器管理控制檯已經可以通過“聯合儲存庫”進行身份認證,但是我們現有的部署在 WebSphere 上面的業務應用系統並不能馬上自動地使用到這一特性。

我們還需要做一定的修改、或者叫做移植工作,才能讓我們的現有的 J2EE Web 應用能夠使用 WAS 的 LDAP 登入機制,進而能夠利用到 WAS 強大的單點登入能力。

讓我們分析一下現有的 J2EE 應用 -- 以 Struts 例子應用 MailReader 為代表,分析一下現有應用常用的認證機制,確定移植目標,對應用進行移植,為它增加標準的 J2EE 表單登入方式,增加新的 J2EE 標準格式的 Form 登入頁面,最後為 Struts 應用增加新的程式碼,在使用者通過新的 J2EE 標準格式的 Form 登入頁面登入以後,後臺應用系統如何為登入使用者構造使用者會話資訊。

在 Struts 網站下載 Struts 1.3.8 的 Example Applications struts-1.3.8-apps.zip,解壓縮後找到 struts-mailreader-1.3.8.war 檔案,使用 WAS V6.1 管理控制檯安把這個 war 檔案安裝並啟動。訪問 http://wp6x.cn.ibm.com:9080/mail/EditSubscription.do,系統提示登入。錄入使用者名稱 user 口令 pass 就可以登入。


圖 9:MailReader 應用登入頁面 

MailReader 的使用者名稱口令等資料儲存在 WEB-INF\database.xml 檔案資料庫中 ( 類似的,企業 J2EE Web 應用的使用者資料大多儲存在關係型資料庫或者是目錄伺服器中 )。使用者每次訪問 MailReader 受限訪問的資源,MailReader 都會呼叫 doGetUser 方法檢視在 J2EE 會話 HttpSession 裡面否有登入使用者的資訊。如果沒有使用者資訊,則或者重定向客戶瀏覽器到使用者註冊頁面(比如訪問 EditRegistration.do),或者是重定向到登入頁面(比如訪問 DeleteSubscription.do?host=mail.yahoo.com)。 doGetUser 方法的程式碼很簡單,就是從 HttSession 物件中查詢屬性名字為常量 Constants.USER_KEY 的 User 物件,如果沒有就說明使用者沒有登入過,更不要說有許可權了。


/** Helper method to obtain User form session (if any).
 * @param request Our HttpServletRequest

 * @return User object, or null if there is no user. */
 protected User doGetUser(HttpServletRequest request) {
 HttpSession session = request.getSession();
 return (User) session.getAttribute(Constants.USER_KEY);
 }


接下來我們看看 MailReader 的登入程式。在下面 LogonAction.java 程式碼的 ActionForward 方法中,首先從 Form 中取得使用者提交的使用者名稱口令,然後呼叫 doGetUser(username, password, errors) 方法進行使用者名稱口令身份驗證,如果驗證通過,呼叫 doCacheUser(request, user) 方法把 User 物件儲存到 HttpSession 中。


// Retrieve user
 String username = doGet(form, USERNAME);
 String password = doGet(form, PASSWORD);
 ActionMessages errors = new ActionMessages();
 User user = doGetUser(username, password, errors);
 ……
 // Cache user object in session to signify logon
 doCacheUser(request, user);


接下來讓我們設定一下移植現有 J2EE 應用到使用 J2EE 表單登入認證和“聯合儲存庫”的移植目標。我們為 MailReader 增加 WAS LDAP 認證的移植目標如下:

  • 原有的 MailReader 使用者資料庫儲存方式不變,登入方式不變。經過移植以後,使用者仍然可以使用原有的使用者名稱和口令,通過 MailReader 原有的登入介面進行登入。
  • 增加新的登入方式,支援使用 WAS LDAP 身份認證,登入方式是標準的 J2EE 表單登入。經過移植以後,使用者可以使用新的 LDAP 使用者名稱和口令通過 MailReader 新的 J2EE 表單登入介面進行登入。
  • 如果使用者提供的使用者名稱口令通過了 WAS LDAP 身份認證,MailReader 信任 WAS 的身份認證資訊不再使用 MailReader 使用者資料庫進行二次使用者名稱口令驗證,而是直接在 MailReader 使用者資料庫查詢使用者的其他資訊並構造出 User 物件,然後放到 HttpSession 裡面,最後重定向使用者的請求到 MailReader 業務操作頁面中。
  • 如果使用者提供的使用者名稱口令通過了 WAS LDAP 身份認證,但是在 MailReader 使用者資料庫中找不到該使用者的其他資訊,也就構造不出來 User 物件,那麼我們需要提示使用者-您輸入的使用者名稱是 LDAP 合法使用者,但不是 MailReader 的合法使用者,請重新登入。

根據上面的移植目標,我們對 Struts 例子應用進行移植,首先為 MailReader 增加標準的 J2EE 表單登入方式,這樣 MailReader 部署到 WAS 上面以後,就可以使用 WAS 的機制通過 LDAP 伺服器進行身份驗證了。 把 struts-mailreader-1.3.8.war 用 jar 或者 winzip 解壓縮到硬碟上,開啟 web.xml 檔案在 </web-app> 標記之前加入如下內容。


<security-constraint>
 <display-name>Secured</display-name>
 <web-resource-collection>
 <web-resource-name>MailReader Demonstration Application</web-resource-name>
 <description>Protection area for MailReader</description>
 <url-pattern>/loginadapter.jsp</url-pattern>
 <http-method>GET</http-method>
 <http-method>POST</http-method>
 </web-resource-collection>
 <auth-constraint>
 <description>All Authenticated users for MailReader</description>
 <role-name>All Role</role-name>
 </auth-constraint>
 <user-data-constraint>
 <transport-guarantee>NONE</transport-guarantee>
 </user-data-constraint>
 </security-constraint>
 <login-config>
 <auth-method>FORM</auth-method>
 <form-login-config>
 <form-login-page>/login.jsp</form-login-page>
 <form-error-page>/failedlogin.jsp</form-error-page>
 </form-login-config>
 </login-config>
 <security-role>
 <description>All Authenticated Users Role.</description>
 <role-name>All Role</role-name>
 </security-role>


web.xml 檔案中 <web-resource-collection> 標記定義了 J2EE 安全保護的資源列表;

<url-pattern> 標記訂了受 J2EE 安全保護的資源的 URL 列表,比如 /loginadapter.jsp;<auth-constraint> 標記說明了有權訪問這個檔案的使用者必須是屬於 All Role 角色的使用者;<login-config><form-login-page> 標記指定使用者可以通過 /login.jsp 登入頁面進行 FORM 表單方式的登入;<form-error-page> 標記指定使用者登入失敗 ( 使用者名稱或者口令錯誤 ) 後 WAS 返回給使用者的錯誤頁面。

前面我們修改了 MailReader 的 web.xml 檔案,增加了標準的 J2EE 表單登入方式的配置。下面我們為 MailReader 定製 J2EE 使用者身份認證的登入介面 --login.jsp。根據 J2EE 的規定,這個登入頁面有一些具體的規定:

  • 必須包括一個 form 元素,form 的 action 屬性必須是 j_security_check,j_security_check 是 J2EE 中專門處理使用者登入的一個小應用程式
  • form 中必須包括兩個文字框,一個名為:j_username,另外一個是:j_password,這兩個文字框的名字不能更改
  • 其他的頁面元素可以隨意定製以滿足客戶個性化、風格化的需求

需要特別指出的是,為控制使用者使用 login.jsp Form 表單登入以後重定向的頁面,我們可以在 login.jsp 裡面設定一個 IBM WAS 專用的 cookie,cookie 名字是 WASReqURL,告訴 WAS 在通過使用者 LDAP 身份認證以後重定向客戶的瀏覽器到指定 URL – loginadapter.jsp。


<%
if (request.getParameter("WASReqURL")!=null) {
 // 獲得要重定向的 URL 引數
 String url = request.getParameter("WASReqURL");
} %>
<script language="JavaScript">
 document.cookie = "WASReqURL=<%=url%>";
</script>


MailReader 移植的最後一步,我們為它增加新的程式碼,在使用者通過新的 J2EE 標準格式的 Form 登入頁面登入以後,後臺應用系統可以根據業務邏輯為登入使用者構造使用者會話資訊。

在 loginadapter.jsp 中,我們首先呼叫標準 J2EE API request.getRemoteUser() 獲得當前登入 LDAP 使用者名稱,我們信任 WAS 的身份驗證,因此就不再訪問 MailReader database 中進行資訊使用者名稱口令的校驗了。接下來使用 MailReader 的 UserDatabase. findUser(username) 方法構造 User 物件,並且把 User 物件儲存到 HttpSession 物件中。最後通過 JavaScript 把頁面重定向到客戶想要訪問的 MailReader 業務頁面。


<%
// getRemoteUser() 是 J2EE Servlet 標準 API,從 J2EE Container 中獲得登入使用者身份
String username = request.getRemoteUser();
//UserDatabase 是 MailReader 的資料庫訪問物件
UserDatabase database = (UserDatabase)session.
 getServletContext().getAttribute(Constants.DATABASE_KEY);
// 從 MailReader 資料庫裡面查詢使用者物件
User user = database.findUser(username);
……
// 把使用者物件 User 儲存到 HttpSession 中
session.setAttribute(Constants.USER_KEY, user);
……
<!-- 使用 JavaScript 重定向客戶瀏覽器到指定的 MailReader 頁面郳 par <script language="JavaScript">
<% 
 String scheme = request.getScheme(); // http
 String serverName = request.getServerName(); // hostname.com
 int serverPort = request.getServerPort(); // 80
 String contextPath = request.getContextPath(); // /mywebapp
 
 // Reconstruct original requesting URL
 String url = scheme+"://"+serverName+":"+serverPort+contextPath;
 url = url + "/EditRegistration.do";
%>
window.location="<%=url%>";
</script>


在解壓縮的 struts war 檔案目錄修改了 web.xml,新增加了 login.jsp、failedlogin.jsp、loginadapter.jsp,現在我們可以使用 java jar 命令把這個目錄再次壓縮打包成 war 檔案。

移植工作結束以後,使用 WAS 管理控制檯部署新的應用。部署以後不要立即啟動應用,還需要在管理控制檯上把 MailReader 新定義的角色 All Role 和“所有已認證的使用者”進行繫結。繫結安全形色和使用者 / 組的對映以後,就可以啟動 MailReader Web 應用,使用 LDAP 的使用者進行登入訪問了。


圖 10:安全形色到使用者 / 組對映 

訪問 http://wp6x.cn.ibm.com:9080/mailreader/loginadapter.jsp,這是一個受 WAS 保護的頁面,WAS 重定向客戶瀏覽器到 J2EE Web 登入頁 http://wp6x.cn.ibm.com:9080/mailreader/login.jsp。

輸入 LDAP 使用者名稱和口令,WAS 伺服器驗證通過以後,如果這個使用者也同時存在於 MailReader 的使用者資料庫裡面,那麼 loginadapter.jsp 根據 LDAP 使用者名稱構造 User 物件放到 HttpSession 裡面,然後重定向瀏覽器到 MailRader 業務頁面 http://wp6x.cn.ibm.com:9080/mailreader/EditRegistration.jsp。


圖 11:MailReader 新的 J2EE 登入頁面 


圖 12:MailReader J2EE 登入後的業務頁面 

輸入 LDAP 使用者名稱和口令,WAS 伺服器驗證通過以後,如果這個使用者不存在於 MailReader 的使用者資料庫裡面,loginadapter.jsp 提示使用者選擇使用新的使用者名稱口令登入。


圖 13:MailReader J2EE 登入的使用者在 MailReader 使用者資料庫中不存在 

移植總結

回顧一下,在以上的移植工作中,我們修改了 Struts 例子應用 MailReader 的 web.xml 檔案,加入新的 FORM 表單登入介面 login.jsp、加入一個簡單的登入錯誤提示頁面 failedlogin.jsp、一個使用者會話構造和重定向頁面 loginadapter.jsp,我們就為原來使用檔案資料庫進行使用者身份驗證的 Struts 應用增加 ( 移植 ) 了使用 IBM WAS LDAP 登入的能力。通常情況下,快的半天,慢的一天的時間就可以成功地把一個企業應用遷移到 IBM WAS(LDAP) 身份認證和登入方式。