構建和實現單點登錄解決方案

     轉自:http://www.ibm.com/developerworks/cn/web/wa-singlesign/

 

級別: 初級

Chris Dunne, 技術主管, Big Picture Software

2007 年 9 月 19 日

在現有的應用程序中實現單點登錄解決方案(single sign-on,SSO,即登錄一次,就可以向所有網絡資源驗證用戶的身份)是非常困難的,但是在構建複雜的門戶時,每個開發人員都要面對這個問題。因爲 門戶需要與後端資源集成,而每個後端資源都有自己的身份驗證需求,所以門戶常常必須向用戶提供單點登錄特性。在本文中,Chris Dunne 一步步地描述了他爲一個 Web 門戶構建單點登錄解決方案的經歷。他將講解如何設置一個開放源碼解決方案(來自 Yale University 的 Central Authentication Service),以及如何將它擴展爲根據 Microsoft Active Directory 基礎設施進行身份驗證。

我在自己的工作中發現,對各種門戶應用程序的需求正在增長。門戶的技術和功能性需求變得越來越複雜了。儘管出現了可以構建簡單門戶的工具,但是門戶與遠程或遺留數據源的集成問題仍然不容易解決。其中一個問題就是身份驗證。

身份驗證是個複雜的問題。門戶需要向後端數據源和應用程序驗證用戶的身份,但是這些應用程序可能具有互不相同的底層安全基礎設施。理想的最高效的身份驗證解決方案是單點登錄(single sign-on,SSO) 解決方案;在這種解決方案中,用戶只需要登錄一次,就可以向所有網絡資源驗證他的身份。

最近,在構建一個需要 SSO 的教育門戶時,我研究了許多商業的和開放源碼的 SSO 解決方案。在本文中,我將一步步地介紹使用免費的 SSO 實現(來自 Yale University 的 CAS)構建簡單 SSO 系統的過程。

爲什麼要選擇單點登錄?

有 多少人實現過自己的身份驗證機制(常常是某種簡單的數據庫查詢)?您是否常常考慮創建和管理用戶帳號所需的工作流?在任何開發項目中,身份驗證都是很常見 的任務。如果幸運的話,公司已經有一些通用的身份驗證類或庫。但是,這個任務常常被忽視,被當作只在後臺發生的微不足道的事情。

一 般來說,公司往往沒有一致的身份驗證策略或可靠的身份驗證框架。隨着時間的推移,這會導致大量應用程序具有自己的身份驗證需求和用戶存儲庫。每個人都需要 記住多個用戶名和密碼,才能訪問網絡上的不同應用程序。這給管理和支持部門帶來很大的負擔 —— 必須在每個應用程序中爲每個職員設置帳號,當用戶忘記密碼時還要幫助他們解決問題,等等。

身份驗證是多種應用程序、平臺和基礎設施共有的需求。一般來說,一個用戶應該不需要多個用戶名。理想情況下,他應該只需要證明自己的身份一次,然後就能夠訪問所有已經得到授權的網絡資源。

SSO 的目標是,讓用戶能夠通過一次登錄訪問所有應用程序。它提供一個統一的機制來管理用戶的身份驗證,並實現業務規則來決定用戶對應用程序和數據的訪問。

在討論單點登錄的技術細節之前,先談談單點登錄的一些好處和風險。好處包括:

  • 提高用戶的效率。用戶不再被多次登錄困擾,也不需要記住多個 ID 和密碼。另外,用戶忘記密碼並求助於支持人員的情況也會減少。
  • 提高開發人員的效率。SSO 爲開發人員提供了一個通用的身份驗證框架。實際上,如果 SSO 機制是獨立的,那麼開發人員就完全不需要爲身份驗證操心。他們可以假設,只要對應用程序的請求附帶一個用戶名,身份驗證就已經完成了。
  • 簡化管理。如果應用程序加入了單點登錄協議,管理用戶帳號的負擔就會減輕。簡化的程度取決於應用程序,因爲 SSO 只處理身份驗證。所以,應用程序可能仍然需要設置用戶的屬性(比如訪問特權)。

對於單點登錄,經常提到的一些問題包括:

  • 難以重構。對 SSO 解決方案進行重構來適應現有的應用程序很困難,很耗費時間而且昂貴。
  • 無人看守的桌面。 實現 SSO 會減少一些安全風險,但是也增加了其他安全風險。例如,如果用戶登錄之後離開了他的機器,惡意用戶就可以訪問他的資源。儘管這是一個普遍存在的安全問題, 但是 SSO 會使這個問題更加嚴重,因爲惡意用戶可以訪問所有獲得授權的資源。在採用多次登錄方式時,用戶每次只能登錄進一個系統,所以只有一個資源被泄露。
  • 單點攻擊。在使用單點登錄時,所有應用程序使用一個集中的身份驗證服務。對於希望實施拒絕服務攻擊的黑客,這是一個有吸引力的目標。

所以,SSO 並非毫無缺點。但是我相信,在用戶、管理員和開發人員看來,它的優點要超過缺點。

 




回頁首


SSO 開放源碼項目

正如前面提到的,我當前正在爲一個教育機構構建 Web 門戶。這個門戶將爲參與遠程課程的學生提供一個在線學習環境。

這個門戶的構造塊已經就位了。站點已經建立了,課程內容已經開發出來了,虛擬學習環境也就位了,輔助應用程序(比如日記、日曆、電子郵件和筆記本)已經構建好或已經取得。所有這些組件都正在由學生使用,而且每個應用程序都運行在自己的服務器上。

客戶現在希望能夠通過 Web 瀏覽器遠程訪問這些應用程序,所以要構建一個門戶來提供訪問應用程序的單一入口,門戶應該向用戶提供單點登錄功能。用戶登錄進門戶之後,對他的身份進行驗證,然後他就能夠訪問門戶中所有已授權的資源。

我決定搜索一下關於如何實現 SSO 方案的信息,尋找有幫助的白皮書、產品和開放源碼項目。我使用以下條件來限制搜索的範圍:

  • Java 實現。這個門戶應用程序基礎設施是基於 Java 的,所以我希望 SSO 實現也是基於 Java 語言的。
  • 容易實現。在這個場景中,容易實現是指不需要對基礎設施或現有的應用程序做大量修改。
  • 其效果已經得到證明。它應該已經被一些大型組織採用,而且仍然處於活躍的開發階段。
  • 與 LDAP 兼容。我們的客戶使用 Microsoft Active Directory Server,所以我需要系統能夠輕鬆地根據 ADS 進行身份驗證。

我很高興地發現了幾個出色的開放源碼項目。(參見 參考資料 中的列表。)

我 開始使用來自 Yale University 的 CAS(Central Authentication Service)系統開發一個原型,因爲它滿足我的所有條件。它是基於 Java 的,源代碼是開放的。只需使用 JSP 標記和 servlet 過濾器,就能夠相當輕鬆地在 Java 應用程序環境中實現它。Yale University 正在使用它,這說明它的品質滿足我的條件。它還允許輕鬆地改變或擴展實際的身份驗證機制(無論是查詢數據庫還是 LDAP 服務器上的用戶名和密碼)。

 




回頁首


CAS 概述

注意,在採用 CAS 協議時,應用程序不會看到用戶的密碼。CAS 服務器執行身份驗證,只有它能夠看到用戶的密碼。這會增強安全性,因爲用戶名和密碼並不通過網絡傳遞給其他應用程序。

下圖說明了在集成了 CAS 服務器的系統中身份驗證是如何執行的。


圖 1. CAS 協議如何執行身份驗證
CAS 協議如何執行身份驗證

下面是這個身份驗證協議中的主要步驟。

  1. 用戶嘗試使用應用程序的 URL 訪問應用程序。用戶被重定向到 CAS 登錄 URL,採用的是 HTTPS 連接,他請求的服務的名稱作爲參數傳遞。這時向用戶顯示一個用戶名/密碼對話框。
  2. 用戶輸入 ID 和密碼,CAS 對他進行身份驗證。如果身份驗證失敗,目標應用程序根本不會知道這個用戶曾經試圖訪問它 —— 用戶在 CAS 服務器上就被攔住了。
  3. 如果身份驗證成功,CAS 就將用戶重定向回目標應用程序,並在 URL 中附加一個稱爲 ticket 的參數。然後,CAS 嘗試創建一個稱爲 ticket-granting cookie 的內存 cookie。這是爲了以後進行自動的重新驗證;如果存在這個 cookie,就表示這個用戶已經成功地登錄了,用戶就不需要再次輸入他的用戶名和密碼。
  4. 然後,應用程序要檢查這個 ticket 是否正確,以及是否代表一個有效用戶;檢查的方法是,打開一個 HTTPS 連接來調用 CAS serviceValidate URL,並作爲參數傳遞 ticket 和服務名稱。CAS 檢查這個 ticket 是否有效,以及是否與請求的服務相關聯。如果檢查成功,CAS 就將用戶名返回給應用程序。

如果採用 Servlet 2.3 規範進行開發,那麼甚至不需要爲這些步驟操心。一個 servlet 過濾器會處理整個協議。您要做的只是在 web.xml 文件中配置過濾器參數。我就採用這種方式 —— 它意味着對門戶中應用程序代碼的修改非常少。

對 CAS 的深入討論超出了本文的範圍,我建議您閱讀 參考資料 中列出的來自 Yale University 的文章,從而判斷這種身份驗證方案是否滿足您的需要。

 




回頁首


CAS 入門

設置 CAS 軟件是非常簡單的,但是在開始設置之前,您應該瞭解我用的一些軟件。我只用 Tomcat 4.1、Java Development Kit 1.4 和 Ant 1.5 測試了 CAS。(可以從 參考資料 下載這裏提到的文件和客戶機庫。)

首先,下載 CAS 服務器和客戶機庫。已經針對許多語言和環境開發了客戶機庫,包括 Java、ASP、Perl、PHP 和 PL/SQL。

CAS 使用 HTTPS,所以必須在 Tomcat 中啓用這個功能。我發現這需要點兒技巧,但是如果按照我提供的說明(readme_tomcat_ssl.txt 文件)去做,應該不困難。

對 CAS 服務器 ZIP 文件進行解壓,並使用 Ant 構建腳本構建 CAS 服務器軟件。將 WAR 文件(Web Archives)部署到 Tomcat 的 /webapps 目錄中。在啓動 Tomcat 時,用 WAR 文件在 Tomcat/webapps 中創建一個 CAS 目錄。

下 載 CAS 客戶機庫。對 ZIP 文件進行解壓,就會看到許多目錄。我要使用的是 Java 客戶機庫。同樣,也提供了 Ant 構建腳本。運行這個構建腳本。這會生成一個稱爲 casclient.jar 的 JAR 文件。將這個文件複製到 Tomcat 根目錄下的 common/lib 目錄中。

現在,需要配置應用程序來使用 CAS。本文中用來進行演示的應用程序是 Tomcat 提供的可靠的 “HelloWorld” servlet 示例。這個應用程序應該在 Tomcat 系統中的 /webapps/examples 目錄下面。修改 web.xml 文件來配置 servlet 過濾器。

HelloWorld JSP 的 web.xml 文件包含下面的 servlet 過濾器配置。它對 HTTPS 使用本地主機和端口 8443。根據自己的配置修改這些設置。我提供的 zip 文件中包含一個 web.xml 文件示例。


清單 1. HelloWorld JSP 的默認 servlet 過濾器配置

				
<filter>
<filter-name>CAS Filter</filter-name>
<filter-class>edu.yale.its.tp.cas.client.filter.CASFilter</filter-class>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.loginUrl</param-name>
<param-value>https://localhost:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>https://localhost:8443/cas/proxyValidate</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>localhost</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

啓動 Tomcat。然後輸入 URL http://localhost/examples/servlet/HelloWorldExample。這時會重定向到 CAS 登錄屏幕。默認的身份驗證器只要求爲用戶名和密碼提供相同的字符串,例如在兩個域中都輸入 demo。然後,就會重定向到 HelloWorld 頁面。

這 只是個簡單的 CAS 演示,但是它說明,通過使用這個強大的 servlet 過濾器,讓現有的 Java servlet 應用程序使用 CAS 是多麼容易。還可以使用 JSP 標記集替代 servlet 過濾器,這種方法適合那些無法使用 servlet 過濾器的應用程序或應用服務器。

 




回頁首


Active Directory Server 身份驗證

在 我的教育門戶項目中,可以很輕鬆地修改應用程序,因爲我能夠得到源代碼。我在每個應用程序中使用了 servlet 過濾器。在這種情況下,當用戶首次訪問門戶並請求一個受限制的應用程序或數據源時,CAS 將執行身份驗證。身份驗證成功之後,CAS servlet 過濾器可以檢查 ticket,並將用戶名作爲參數傳遞給請求的應用程序。我修改了現有的應用程序登錄屏幕,讓它們在成功的身份驗證之後從 CAS 接收用戶名參數。然後,應用程序可以使用這個參數對用戶進行授權,並對用戶的活動進行審計記錄。

但是,CAS 本身沒有附帶任何真正有用的身份驗證器。默認的身份驗證器僅僅檢查用戶名和密碼是否相同。在教育門戶項目中,我使用 Microsoft 的 Active Directory Server 存儲用戶的個人信息。所以我需要擴展 CAS 服務器,讓它根據 Active Directory Server 檢查用戶名和密碼。

Yale University 的 CAS 設計人員開發了一個可插入的身份驗證器架構。通過擴展 PasswordHandler 接口,開發人員可以創建一個類來實現密碼檢查邏輯。


清單 2. CAS 的可插入身份驗證器架構

				
/** Interface for password-based authentication handlers. */
public interface PasswordHandler extends AuthHandler {

/**
* Authenticates the given username/password pair, returning true
* on success and false on failure.
*/
boolean authenticate(javax.servlet.ServletRequest request,
String username,
String password);

}

 

我要做的只是瞭解如何使用 Active Directory Server 檢查用戶名和密碼,創建一個類來擴展這個接口,並添加執行身份驗證的代碼。

ADS 是一種與 LDAP3 兼容的目錄服務器。它支持許多不同的身份驗證方法,主要包括匿名(anonymous)簡單(simple)SASL(Simple Authentication and Security Layer)。匿名身份驗證不適合我的項目;簡單身份驗證用明文發送密碼,這也不滿足需要。所以我使用 SASL。

SASL 支持可插入的身份驗證。這意味着可以對 LDAP 客戶機和服務器進行配置,讓它們相互協商並使用多種身份驗證機制之一。下面是當前定義的一部分 SASL 機制:

  • Anonymous
  • CRAM-MD5
  • Digest-MD5
  • External
  • Kerberos V4
  • Kerberos V5
  • SecurID
  • Secure Remote Password
  • S/Key
  • X.509

在這些機制中,流行的 LDAP 服務器(比如 Sun、OpenLDAP 和 Microsoft 提供的服務器)都支持 External、Digest-MD5 和 Kerberos V5。

CAS 本身與 Kerberos 相似,它們有許多相同的概念,比如 ticket 和 ticket-granting ticket(在 CAS 中實際上稱爲 ticket-granting cookie),它們的協議也是相似的。所以,選擇 Kerberos 機制似乎是很自然的。另外,添加對 Kerberos 身份驗證的支持讓 CAS 在未來的開發中能夠作爲基於 Web 的 Kerberos 代理代表用戶,這使 CAS 能夠替用戶管理 Kerberos ticket 的所有方面。這意味着遠程用戶也可以使用相同的 Kerberos 身份驗證機制訪問本地和網絡資源。

Kerberos 協議已經內置在 ADS 和 Windows 2000/XP 中。Java Authentication and Authorization Service(JAAS)提供了 Kerberos Login Module 的一個實現(參考資料 中列出的一個教程詳細描述瞭如何建立一個示例應用程序)。精確地說,它是 GSS-API SASL 機制,但是它只爲 LDAP3 服務器提供 Kerberos v5 身份驗證。

Kerberos 身份驗證是一個很簡單的過程(如果仔細地按照以下說明進行操作的話):

  1. 在 JAAS 配置文件中爲應用程序類配置 Login Module。
    edu.yale.its.tp.cas.auth.provider.KerberosAuthHandler  
    {
    com.sun.security.auth.module.Krb5LoginModule required client=TRUE;
    };
  2. 創建一個 LoginContext,傳遞執行身份驗證的類的名稱和 CallBackHandler 對象。
    LoginContext lc = new LoginContext(CASApp.class.getName(), 
    new CASCallbackHandler());
  3. 調用 login() 方法來執行身份驗證。如果執行過程沒有異常,身份驗證就成功了。如果拋出了異常,那麼這個異常會指出失敗的原因。

要想深入瞭解 Kerberos 身份驗證,建議您利用本文末尾的參考資料。(我還提供了自己的實現和配置文件,KerberosAuthHandlerCASCallBackHandler。)

需要創建一個新的 PasswordHandler 實現,KerberosAuthHandler,它按照上面的方法根據使用 Kerberos v5 的 Active Directory Server 進行身份驗證。


清單 3. 創建新的 PasswordHandler 實現

				
public class KerberosAuthHandler implements PasswordHandler {

public boolean authenticate(javax.servlet.ServletRequest request,
String username,
String password)
{

LoginContext lc = null;

try
{
/* Set up the Callback handler, and initialise */
/* the userid and password fields */

CASCallbackHandler ch = new CASCallbackHandler();
ch.setUserId(username);
ch.setPassword(password);

/* Initialise the login context - LoginModule configured */
/* in cas_jaas.conf and */
/* set to use Krb5LoginModule. */
lc = new LoginContext(KerberosAuthHandler.class.getName(), ch);

/* Perform the authentication */
lc.login();

}
catch (LoginException le)
{
System.err.println("Authentication attempt failed" + le);
return false;
}
return true;

}
}

 

在創建 LoginContext 時,傳遞這個類名和 CASCallbackHandler 對象。JAAS 配置文件指定這個類使用的登錄模塊。

在調用 login() 方法時,登錄模塊知道爲了進行身份驗證它需要從用戶/應用程序獲得哪些信息。在使用 Kerberos 時,它需要用戶名和密碼,所以它構造一個數組,其中包含兩個回調對象(NameCallbackPasswordCallback),然後調用 CallbackHandler 對象,這個對象決定如何執行這些回調並獲得用戶名和密碼。

我採用最簡單最有利的方式,使用 CallbackHandler 上的 setter 方法顯式地設置用戶 ID 和密碼。然後,CallbackHandler 將它們傳遞給 NameCallbackPasswordCallback 對象。


清單 4. 用 setName (CASUserId) 和 setPassword (CASPassword) 設置 ID 和密碼

				
public class CASCallbackHandler implements CallbackHandler
{

private String CASUserId;
private char [] CASPassword;

public void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback cb = (NameCallback)callbacks[i];
cb.setName(CASUserId);

} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback cb = (PasswordCallback)callbacks[i];
cb.setPassword(CASPassword);

} else {
throw new UnsupportedCallbackException(callbacks[i]);
}
}
}

public void setUserId(String userid)
{
CASUserId = userid;
}

public void setPassword(String password)
{
CASPassword = new char[password.length()];
password.getChars(0, CASPassword.length, CASPassword, 0);
}

 

下面要做的是讓 CAS 使用新的身份驗證處理器。方法是在 webapps/cas 中 CAS 服務器的 web.xml 文件中進行以下設置:


清單 5. 讓 CAS 使用新的身份驗證處理器

				
<!-- Authentication handler -->
<context-param>
<param-name>edu.yale.its.tp.cas.authHandler</param-name>
<param-value>
edu.yale.its.tp.cas.auth.provider.KerberosAuthHandler
</param-value>
</context-param>

 

ZIP 文件(參考資料 中的 KerberosAuthSrc.zip)中包含一個 web.xml 示例。

必 須重新啓動 Tomcat,但是這一次還需要設置一些 Java 運行時屬性。對 ZIP 文件(KerberosAuthSrc.zip)進行解壓,並將文件 cas_jaas.conf、krb5.conf 和 setkerberosjvmoptions.bat 複製到 TOMCAT_HOME 目錄中。運行 setkerberosjvmoptions.bat,然後啓動 Tomcat。

現在,可以再用 HelloWorld 應用程序做實驗了。這一次,可以使用 Active Directory Server 中定義的有效的 Kerberos 用戶名和密碼對。

 




回頁首


結束語

如果沒有統一的策略,開發人員就要爲每個網絡應用程序重複實現定製的安全機制。這會導致各種可伸縮性和維護問題。單點登錄解決方案爲安全性和身份驗證提供了統一的框架,這大大減輕了用戶、管理員和開發人員的負擔。

單 點登錄的概念、技術和蘊涵對用戶和管理員而言很複雜,我在本文中只觸及了這個領域的皮毛。但是,我解釋瞭如何使用 Yale University 的 CAS 系統實現一個單點登錄方案,還詳細描述瞭如何擴展這種技術,從而對 LDAP 服務器用戶(具體地說,是使用 Kerberos 協議的 Active Directory Server)進行身份驗證。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章