簡介
雇員工作空間系列 的第 5 部分和第 6 部分將描述如何創建 Document Management portlet 來訪問 DB2 Content Manger。然而,這兩部分並沒有詳細討論身份驗證。本文將簡要地討論 Document Management portlet 中所實現的身份驗證方法,並展示如何使用另一種身份驗證方法來實現單點登錄(single sign-on)功能。
系統環境
文中使用了下列產品:
Lotus Workplace for Multiplatforms Version 2.01
IBM DB2 Content Manager for Multiplatforms Version 8.2
IBM DB2 Information Integrator for Content Version 8.2
IBM WebSphere Application Server Version 5.1
IBM WebSphere Studio Application Developer Version 5.1.2
IBM Portal Toolkit Version 5.0.2
IBM DB2 Universal Database Enterprise Version 8.1
本文中的系統環境與雇員工作空間系列中使用的環境之間的主要不同是:我們使用的是 Lotus Workplace 2.01,而非 WebSphere Portal Server 5.0。Lotus Workplace 服務器 2.0.1 是運行在 WebSphere Portal Server 5.0 之上的應用服務器。它提供了一個集成的企業工作環境,該環境允許用戶管理郵件、發送即時消息和運行 portlet 應用程序。將 Document Management portlet 遷移到 Lotus Workplace 不需要修改任何代碼。
所有的用戶信息都存儲在一個 LDAP 用戶注冊表中,以使 Lotus Workplace 和 DB2 Content Manager 中的用戶信息保持同步。對於本文中所使用的身份驗證方法來說,這是必需的。
憑證保險庫
DB2 Content Manager Server 是作為後端服務器與 portlet 集成的,因此當您使用 portlet 訪問 DB2 Content Manager 時,需要在服務器上進行身份驗證。進行身份驗證的方法之一就是通過使用用戶名和密碼來使用 DB2 Content Manager API。在連接 DB2 Content Manager Server 時,portlet 需要提示輸入用戶名和密碼,或提示使用 portlet 中存儲的用戶名和密碼。
Document Management portlet 在 portlet 的憑證保險庫(Credential Vault)中存儲憑證(credential)—— 如用戶名和密碼。憑證是由 portlet 中的每位用戶維護的,必須與用戶注冊表同步。這就向用戶隱藏了 DB2 Content Manager 的一些與登錄有關的復雜問題。
然而,該方法也有一些缺點。首先,它需要為用戶提供用戶接口,以維護其 DB2 Content Manager 憑證。其次,在 portlet 和 DB2 Content Manager 服務器共享同一用戶注冊表服務器時,讓用戶手工控制憑證與用戶注冊表之間的同步也不是很方便。最後,它將憑證暴露為用戶名和密碼,這可能引入一些安全缺陷。
為了避免這些缺陷,您可以使用下面將要討論的輕量級第三方認證(Light-weight Third Party Authentication,LTPA)令牌方法來提供單點登錄功能。
LTPA 令牌
可以使用 LTPA 令牌在服務器之間提供單點登錄功能。在將 Lotus Workplace 服務器所在的 WebSphere Application 服務器配置為使用 LTPA 令牌進行單點登錄時,LTPA 令牌(是一個 cookIE)將包含已驗證用戶的憑證。
除了使用用戶名和密碼連接 DB2 Content Manager 服務器的 Java API 之外,DB2 Information Integrator for Content Java API 還提供了非可視(non-visual)的 bean,可以使用 LTPA 令牌作為身份驗證的憑證來連接 DB2 Content Manager 服務器。非可視的 bean 不需要用戶手工維護其憑證,也不需要將憑證暴露為用戶名和密碼。但它需要 Lotus Workplace 和 DB2 Content Manager 都接受 LTPA 令牌,以便進行單點登錄。
配置 Lotus Workplace 和 DB2 Content Manager
Lotus Workplace 2.0.1
正如上面所提到的,Lotus Workplace 服務器是運行在 WebSphere Portal 服務器之上,而 WebSphere Portal 服務器是運行在 WebSphere Application 服務器上。為了給 Lotus Workplace 服務器配置單點登錄,需要啟用 WebSphere Application 服務器的單點登錄。關於詳細的指令,請參閱 Lotus Workplace Information Center 中的小節“Configuring Lotus Workplace products for seamless authentication”(請參閱 參考資料)。
DB2 Content Manager 8.2
為了與使用單點登錄功能的 Lotus Workplace 進行集成,需要使用 Content Manager System Administration ClIEnt 在 DB2 Content Manager 服務器中執行下列配置:
啟用 Content Manager 服務器中的單點登錄功能。
創建新的權限集,其中包含 AllowConnectToLogon 和 AllowTrustedLogon 權限,以及其他任何必要權限(例如,ClIEntPrint)。
提示:為了基於現有的權限集創建一個新的權限集,可以右擊現有權限集,然後選擇 Copy。這將打開一個新窗口,您可以在該窗口中通過添加新權限並同時保留現有權限來創建一個新的權限集。
將新創建的權限集分配給需要通過單點登錄功能登錄到 Content Manager 服務器的用戶帳號。
使用 eClIEnt 來驗證單點登錄功能是否起作用。
開發使用 LTPA 令牌的單點登錄 portlet
這一節將闡述如何為 DB2 Content Manager 開發使用 LTPA 令牌的單點登錄 portlet。第一步是一步步地創建一個新的單點登錄 portlet,而不是直接修改 Document Management portlet。接著,要找出更改 portlet 中的哪些地方的代碼,這樣,通過使用 LTPA 令牌,這些 portlet 將具有單點登錄功能。
創建新的單點登錄 portlet
首先,按照下列步驟創建新 portlet 的骨架(skeleton):
在 WebSphere Studio Application Developer 中,選擇 File > New > Project。然後單擊 Next。
選擇左邊面板中的 Portlet Development,然後選擇右邊面板中的 Portlet Project。最後單擊 Next。
在 Project name 字段中輸入 CMSSO,然後選擇 Basic portlet 作為新 portlet 的類型,並選中 Configure advanced options 復選框。最後單擊 Next。
在 EAR project 字段中輸入 CMSSOEAR,為 CMSSO 選擇 Content root,並選擇 J2EE level 1.3WebSphere Portal 5.0。然後單擊 Finish。
在 Portlet Perspective 視圖中,選擇 Window > Preference。
在左邊面板中選擇 Java > Classpath Variable。然後單擊右邊面板中的 New。
在 Name 字段中輸入 CMBROOT。在 Path 字段中輸入 Content Manager 的安裝路徑(例如:X:Program FilesIBMCM82)。然後單擊 OK。
在 Name 字段中輸入 CMBHOME。在 Path 字段中輸入 Content Manager 的公共文件(common files)的目錄名(例如:X:Program FilesIBMCMgmt)。然後單擊 OK。
在 Portlet Perspective 視圖中,右擊 CMSSO 項目並選擇 PropertIEs。
單擊 Add Variable。選擇 CMROOT 並單擊 Extend。然後選擇 libcmb81.jar 並單擊 OK。
單擊 Add Variable。選擇 CMROOT 並單擊 Extend。然後選擇 libcmbsdk81.jar 並單擊 OK。
單擊 Add Variable。然後選擇 CMHOME 並單擊 OK。
在以上步驟中,您創建了新的示例 portlet,並向項目類路徑(classpath)添加了一些 Content Manager 的 Java 庫(cmb81.jar 和 cmbsdk81.jar)。現在,就可以導入示例 Java 程序 TConnect.java 和 TListEntities.java(與 Content Manager 捆綁在一起),來測試該 portlet 的 Content Manager 身份驗證了。TConnect.java 是用來連接 Content Manager 服務器的實用程序類。TListEntitIEs.java 顯示 Content Manager 服務器上所定義實體類型的細節。請按照下列步驟將這兩個 Java 文件與 portlet 進行集成。
在 Portlet Perspective 視圖中,選擇 CMSSO project > Java Resources > cmsso。然後右擊 cmsso 並選擇 Import。最後找出文件 TConnect.java 和 TListEntitIEs.Java,並導入它們。
在上面的兩個文件開頭的地方添加 package cmsso,並對它們進行編譯。
在 CMSSOPortlet.Java 文件中,請按照下列內容修改 actionPerformed() 函數。
if (FORM_ACTION.equals(actionString)) {
// Set form text in the session bean
sessionBean.setFormText(request.getParameter(TEXT));
// Replace the parameters with the proper values
String [] args ={"ICM","ICMNLSDB","icmadmin","passWord"};
try {
TListEntitIEs.main(args);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
System.out.println("<------Submit button pressed--->");
將項目導出為 WAR 文件,並將它部署到 Lotus Workplace 服務器上。
將 Content Manger 公共目錄添加到 Portal 類路徑中。從 WebSphere Administrative Console 中選擇 Environment > Shared LibrarIEs,並單擊 WPSLIB。然後向 CLASSPATH 添加 Content Manager 和 Information Integrator for Content 共享的公共文件(common files)所在的目錄(例如:D:IBMCmgmt),最後單擊 OK。
重啟服務器,並登錄至 Lotus Workplace。加載包含 portlet 的頁面,並單擊 Submit 按鈕,查看日志文件是否輸出了 Content Manager 服務器上定義的實體類型。
如果該 portlet 正常工作,那麼可以開始修改代碼來添加 LTPA 令牌驗證了。DB2 Information Integrator for Content Java API 提供了非可視的 bean,即 CMBConnection,您可以用它來連接使用 LTPA 令牌作為驗證憑證的 DB2 Content Manager Server。
CMBConnection 的方法 connectWithCredential() 接受三個參數:服務器名(server name)、憑證對象(credential object)和連接字符串(connect string)。為了在連接 Content Manager 服務器時啟用 LTPA 令牌作為憑證,需要添加下列代碼,將 Content Manager 服務器連接到 TConnect.Java:
/**
* Connects to a given server using LTPA token.
* @param connection an instance of CMBConnection bean
* @param dstype the datastore type.
* @param server the server name. A connection string can
* also be specifIEd following the name, in parenthesis
* @param ltpaToken the ltpaToken.
*/
public static void connect(CMBConnection connection, String dstype,
String server, String ltpaToken)
throws Exception {
// If the server name is followed by a parenthesized string,
// use that string for the connect string.
if (server.indexOf("(") > 0) {
String connectString = server.substring(server.indexOf("(")+1);
server = server.substring(0, server.indexOf("("));
if (connectString.endsWith(")")) {
connectString = connectString.substring(0,
connectString.length() - 1);
}
connection.setConnectString(connectString);
}
// Set propertIEs on connection bean
connection.setDsType(dstype);
// Use the connect method to connect. This will
// create the correct type of DKDatastore object (see Java API)
// and call its connect method.
System.out.println("Connecting to server");
connection.connectWithCredential(server, ltpaToken, "");
System.out.println("OK, Connected");
// Enable display names support. This will cause
// CMBSchemaManagement, CMBEntity, CMBAttribute, CMBItem to use
// display names when working with CMv8 (default is to use
// non-display names).
connection.setDisplayNamesEnabled(true);
}
為了使用 TConnect.java 中的新 connect() 方法,還需要修改 TListEntitIEs.Java 的 main() 函數。下面是新的 main() 方法的代碼:
public static void main(String[] args) throws Exception {
if (args.length < 3) {
System.out.println(
"Displays details on types of entitIEs defined on the server. n"
+ "Syntax: n"
+ " Java TListEntitIEs <dstype> <server> <ltpaToken> [<entity>] n"
+ "where: n"
+ " <dstype> is the type of server (Fed, ICM, DL, OD, DB2) n"
+ " <server> is the name of the server or database. This can be n"
+ " followed by a connect string in parenthesis. n"
+ " <ltpaToken> is the ltpaToken n"
+ " <entity> optional, the name of a particular entity. Detailed n"
+ " information will be displayed about the entity. n"
+ "Examples: n"
+ " Java TListEntitIEs ICM icmnlsdb ltpaToken n"
+ " Java TListEntitIEs ICM icmnlsdb ltpaToken 'Document (for Document data model)' n"
+ " Java TListEntitIEs DL libsrvrn ltpaToken n"
+ " Java TListEntitIEs Fed cmbdb ltpaToken n"
+ " Java TListEntitIEs DB2 sample ltpaToken emp_photo n"
+ " Java TListEntitIEs OD odserver ltpaToekn n"
+ "where single quotes are used around entity and attribute names that contain spaces");
System.exit(0);
}
String dstype = args[0];
String server = args[1];
String ltpaToken = args[2];
String entity = null;
int nextArg = 4;
if (nextArg < args.length && args[nextArg] != null) {
if (args[nextArg].startsWith("'"))
{
String arg = args[nextArg];
while (nextArg <args.length && !arg.endsWith("'"))
{
nextArg++;
arg += " "+args[nextArg];
}
entity = arg.substring(1,arg.length()-1);
}
else
entity = args[nextArg];
}
CMBConnection connection = new CMBConnection();
CMBSchemaManagement schemaManagement = connection.getSchemaManagement();
schemaManagement.setChildComponentsAsAttributes(childComponentsAsAttributes);
TConnect.connect(connection, dstype, server, ltpaToken);
if (entity == null) {
listEntitIEs(schemaManagement);
} else {
printEntityDetails(schemaManagement.getEntity(entity), 1, true);
}
TConnect.disconnect(connection);
}
為了調用 TListEntities.java 的新 main() 方法,需要修改 CMSSOPortlet.java 的 actionPerformed() 方法。首先,獲取 LTPA 令牌。然後將它傳遞給 main() 函數。LTPA 令牌是一個 cookIE,可以從 PortletRequest Java 對象中檢索它。下面是用來檢索 LTPA 令牌和調用 TListEntitIEs.main() 的代碼:
...
Cookie[] cookies = request.getCookIEs();
String LtpaToken = null;
if (cookies != null && cookIEs.length > 0) {
for (int i = 0; i < cookIEs.length; i++) {
if (cookIEs[i].getName().equalsIgnoreCase("LtpaToken")) {
LtpaToken = cookIEs[i].getValue();
}
}
}
String [] args ={"ICM","ICMNLSDB",""};
args[2] = LtpaToken;
try {
TListEntitIEs.main(args);
} catch (Exception e) {
e.printStackTrace(System.out);
}
System.out.println("<------ Submit button pressed. --->");
...
現在,您已經完成了單點登錄 portlet。您可以構建項目,將該項目導出為 WAR 文件,並將它部署到 Lotus Workplace 服務器上。
用單點登錄增強 Document Management portlet
在測試了上面所創建的新單點登錄 portlet 之後,就可以開始增強本系列第 5 部分和第 6 部分中開發的 Document Management portlet 了。
添加單點登錄功能的過程包含兩個步驟。首先,需要添加一個函數,用該函數來連接使用 LTPA 令牌的 Content Manager 服務器。其次,需要修改 portlet 中的代碼,其中用戶和密碼是從憑證保險庫中檢索獲得的,將用於連接 Content Manager 服務器。
與 TConnect.java 示例相似,Document Management portlet 也有一個實用程序類,其中包含所有的連接和斷開連接的函數。這個類就是 ICMConnectDisconnect。您需要將 TConnect.java 導入 com.ibm.bsd.util 包,並向 ICMConnectDisconnect.Java 文件添加下列代碼:
public static DKDatastoreICM connectWithCredential(String
userName, String ltpaToken)
throws DKException, CMBException, Exception {
// retrIEve connection parameters from property file
PropertyResourceBundle bundle =
(PropertyResourceBundle) PropertyResourceBundle.getBundle(
BSDConstants.PROPERTY_FILE_NAME);
String database =
bundle.getString(BSDConstants.PROPERTY_DATABASE_NAME);
CMBConnection connBean = new CMBConnection();
// you need to set userid for some CM Operations to work
connBean.setUserid(userName);
System.out.println(
"Connecting to datastore (Database '"
+ database
+ "', LtpaToken '"
+ ltpaToken
+ "'");
//connBean.connect();
TConnect.connect(connBean, "ICM", database, ltpaToken);
/*
connBean.setDsType("ICM");
connBean.connectWithCredential("database", ltpaToken, "");
*/
System.out.println("OK, connected");
DKDatastoreICM dsICM =
(com.ibm.mm.sdk.server.DKDatastoreICM)connBean.getDatastore();
System.out.println(
"Connected to datastore (Database '"
+ dsICM.datastoreName()
+ "', LtpaToken '"
+ ltpaToken
+ "').");
return (dsICM);
}
public static String getLtpaToken(PortletRequest request) {
Cookie[] cookies = request.getCookIEs();
String ltpaToken = null;
if (cookies != null && cookIEs.length > 0) {
for (int i = 0; i < cookIEs.length; i++) {
if (cookIEs[i].getName().equalsIgnoreCase("LtpaToken")) {
ltpaToken = cookIEs[i].getValue();
}
}
}
return ltpaToken;
}
為了定位需要進行修改的代碼,可以搜索 getPassWord4CMFromCredential,並用下列代碼替換相關的代碼:
...
//get the portal user id from the portal request object
String userId = request.getUser().getUserID() ;
//extract the passWord of the portal user from the portal credentials
//String passWord =
//PortletCredential4ICMUtil.getPassWord4CMFromCredential(request,
//PortletCredential4ICMUtil.getVault(getPortletConfig().getContext()),
//userId);
String passWord = "";
//connect using the LTPA token
String ltpaToken = ICMConnectDisconnect.getLtpaToken(request);
DKDatastoreICM store =
ICMConnectDisconnect.connectWithCredential(userId, ltpaToken);
// DKDatastoreICM store =
// ICMConnectDisconnect.connect(userId,passWord) ;
sessionBean.setDataStore(store) ;
...
現在,您應該可以使用 Document Management portlet 且無需維護用戶名和密碼了。
結束語
對於客戶來說,Content Manager 服務器和 portlet 之間的無縫集成是一項重要功能。本文討論了將這兩種產品與單點登錄功能進行集成的不同方法,並提供了關於如何配置和開發使用 LTPA 令牌的單點登錄 portlet 的詳細說明。本文還提供了關於修改 Document Management portlet 來使用 LTPA 令牌方法的指南。