單點登錄(Single Sign On , 簡稱 SSO )是目前比較流行的服務于企業(yè)業(yè)務整合的解決方案之一, SSO 使得在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。CAS(Central Authentication Service)是一款不錯的針對 Web 應用的單點登錄框架,本文介紹了 CAS 的原理、協議、在 Tomcat 中的配置和使用,對于采用 CAS 實現輕量級單點登錄解決方案的入門讀者具有一定指導作用。
CAS 是 Yale 大學發(fā)起的一個開源項目,旨在為 Web 應用系統提供一種可靠的單點登錄方法,CAS 在 2004 年 12 月正式成為 JA-SIG 的一個項目。CAS 具有以下特點:
- 開源的企業(yè)級單點登錄解決方案。
- CAS Server 為需要獨立部署的 Web 應用。
- CAS Client 支持非常多的客戶端(這里指單點登錄系統中的各個 Web 應用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。
從結構上看,CAS 包含兩個部分: CAS Server 和 CAS Client。CAS Server 需要獨立部署,主要負責對用戶的認證工作;CAS Client 負責處理對客戶端受保護資源的訪問請求,需要登錄時,重定向到 CAS Server。圖1 是 CAS 最基本的協議過程:
圖 1. CAS 基礎協議

CAS Client 與受保護的客戶端應用部署在一起,以 Filter 方式保護受保護的資源。對于訪問受保護資源的每個 Web 請求,CAS Client 會分析該請求的 Http 請求中是否包含 Service Ticket,如果沒有,則說明當前用戶尚未登錄,于是將請求重定向到指定好的 CAS Server 登錄地址,并傳遞 Service (也就是要訪問的目的資源地址),以便登錄成功過后轉回該地址。用戶在第 3 步中輸入認證信息,如果登錄成功,CAS Server 隨機產生一個相當長度、唯一、不可偽造的 Service Ticket,并緩存以待將來驗證,之后系統自動重定向到 Service 所在地址,并為客戶端瀏覽器設置一個 Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新產生的 Ticket 過后,在第 5,6 步中與 CAS Server 進行身份合適,以確保 Service Ticket 的合法性。
在該協議中,所有與 CAS 的交互均采用 SSL 協議,確保,ST 和 TGC 的安全性。協議工作過程中會有 2 次重定向的過程,但是 CAS Client 與 CAS Server 之間進行 Ticket 驗證的過程對于用戶是透明的。
另外,CAS 協議中還提供了 Proxy (代理)模式,以適應更加高級、復雜的應用場景,具體介紹可以參考 CAS 官方網站上的相關文檔。
本文中的例子以 tomcat5.5 為例進行講解,下載地址:
http://tomcat.apache.org/download-55.cgi
到 CAS 官方網站下載 CAS Server 和 Client,地址分別為:
http://www.ja-sig.org/downloads/cas/cas-server-3.1.1-release.zip
http://www.ja-sig.org/downloads/cas-clients/cas-client-java-2.1.1.zip
![]() ![]() |
![]()
|
CAS Server 是一套基于 Java 實現的服務,該服務以一個 Java Web Application 單獨部署在與 servlet2.3 兼容的 Web 服務器上,另外,由于 Client 與 CAS Server 之間的交互采用 Https 協議,因此部署 CAS Server 的服務器還需要支持 SSL 協議。當 SSL 配置成功過后,像普通 Web 應用一樣將 CAS Server 部署在服務器上就能正常運行了,不過,在真正使用之前,還需要擴展驗證用戶的接口。
在 Tomcat 上部署一個完整的 CAS Server 主要按照以下幾個步驟:
如果希望 Tomcat 支持 Https,主要的工作是配置 SSL 協議,其配置過程和配置方法可以參考 Tomcat 的相關文檔。不過在生成證書的過程中,會有需要用到主機名的地方,CAS 建議不要使用 IP 地址,而要使用機器名或域名。
CAS Server 是一個 Web 應用包,將前面下載的 cas-server-3.1.1-release.zip 解開,把其中的 cas-server-webapp-3.1.1.war 拷貝到 tomcat的 webapps 目錄,并更名為 cas.war。由于前面已配置好 tomcat 的 https 協議,可以重新啟動 tomcat,然后訪問:https://localhost:8443/cas ,如果能出現正常的 CAS 登錄頁面,則說明 CAS Server 已經部署成功。
雖然 CAS Server 已經部署成功,但這只是一個缺省的實現,在實際使用的時候,還需要根據實際概況做擴展和定制,最主要的是擴展認證 (Authentication) 接口和 CAS Server 的界面。
CAS Server 負責完成對用戶的認證工作,它會處理登錄時的用戶憑證 (Credentials) 信息,用戶名/密碼對是最常見的憑證信息。CAS Server 可能需要到數據庫檢索一條用戶帳號信息,也可能在 XML 文件中檢索用戶名/密碼,還可能通過 LDAP Server 獲取等,在這種情況下,CAS 提供了一種靈活但統一的接口和實現分離的方式,實際使用中 CAS 采用哪種方式認證是與 CAS 的基本協議分離開的,用戶可以根據認證的接口去定制和擴展。
擴展 AuthenticationHandler
CAS 提供擴展認證的核心是 AuthenticationHandler 接口,該接口定義如清單 1 下:
清單 1. AuthenticationHandler定義
public interface AuthenticationHandler { /** * Method to determine if the credentials supplied are valid. * @param credentials The credentials to validate. * @return true if valid, return false otherwise. * @throws AuthenticationException An AuthenticationException can contain * details about why a particular authentication request failed. */ boolean authenticate(Credentials credentials) throws AuthenticationException; /** * Method to check if the handler knows how to handle the credentials * provided. It may be a simple check of the Credentials class or something * more complicated such as scanning the information contained in the * Credentials object. * @param credentials The credentials to check. * @return true if the handler supports the Credentials, false othewrise. */ boolean supports(Credentials credentials); } |
該接口定義了 2 個需要實現的方法,supports ()方法用于檢查所給的包含認證信息的Credentials 是否受當前 AuthenticationHandler 支持;而 authenticate() 方法則擔當驗證認證信息的任務,這也是需要擴展的主要方法,根據情況與存儲合法認證信息的介質進行交互,返回 boolean 類型的值,true 表示驗證通過,false 表示驗證失敗。
CAS3中還提供了對AuthenticationHandler 接口的一些抽象實現,比如,可能需要在執(zhí)行authenticate() 方法前后執(zhí)行某些其他操作,那么可以讓自己的認證類擴展自清單 2 中的抽象類:
清單 2. AbstractPreAndPostProcessingAuthenticationHandler定義
public abstract class AbstractPreAndPostProcessingAuthenticationHandler implements AuthenticateHandler{ protected Log log = LogFactory.getLog(this.getClass()); protected boolean preAuthenticate(final Credentials credentials) { return true; } protected boolean postAuthenticate(final Credentials credentials, final boolean authenticated) { return authenticated; } public final boolean authenticate(final Credentials credentials) throws AuthenticationException { if (!preAuthenticate(credentials)) { return false; } final boolean authenticated = doAuthentication(credentials); return postAuthenticate(credentials, authenticated); } protected abstract boolean doAuthentication(final Credentials credentials) throws AuthenticationException; } |
AbstractPreAndPostProcessingAuthenticationHandler 類新定義了 preAuthenticate() 方法和 postAuthenticate() 方法,而實際的認證工作交由 doAuthentication() 方法來執(zhí)行。因此,如果需要在認證前后執(zhí)行一些額外的操作,可以分別擴展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成為了子類必須要實現的方法。
由于實際運用中,最常用的是用戶名和密碼方式的認證,CAS3 提供了針對該方式的實現,如清單 3 所示:
清單 3. AbstractUsernamePasswordAuthenticationHandler 定義
public abstract class AbstractUsernamePasswordAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler{ ... protected final boolean doAuthentication(final Credentials credentials) throws AuthenticationException { return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials); } protected abstract boolean authenticateUsernamePasswordInternal( final UsernamePasswordCredentials credentials) throws AuthenticationException; protected final PasswordEncoder getPasswordEncoder() { return this.passwordEncoder; } public final void setPasswordEncoder(final PasswordEncoder passwordEncoder) { this.passwordEncoder = passwordEncoder; } ... } |
基于用戶名密碼的認證方式可直接擴展自 AbstractUsernamePasswordAuthenticationHandler,驗證用戶名密碼的具體操作通過實現 authenticateUsernamePasswordInternal() 方法達到,另外,通常情況下密碼會是加密過的,setPasswordEncoder() 方法就是用于指定適當的加密器。
從以上清單中可以看到,doAuthentication() 方法的參數是 Credentials 類型,這是包含用戶認證信息的一個接口,對于用戶名密碼類型的認證信息,可以直接使用 UsernamePasswordCredentials,如果需要擴展其他類型的認證信息,需要實現Credentials接口,并且實現相應的 CredentialsToPrincipalResolver 接口,其具體方法可以借鑒 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。
JDBC 認證方法
用戶的認證信息通常保存在數據庫中,因此本文就選用這種情況來介紹。將前面下載的 cas-server-3.1.1-release.zip 包解開后,在 modules 目錄下可以找到包 cas-server-support-jdbc-3.1.1.jar,其提供了通過 JDBC 連接數據庫進行驗證的缺省實現,基于該包的支持,我們只需要做一些配置工作即可實現 JDBC 認證。
JDBC 認證方法支持多種數據庫,DB2, Oracle, MySql, Microsoft SQL Server 等均可,這里以 DB2 作為例子介紹。并且假設DB2數據庫名: CASTest,數據庫登錄用戶名: db2user,數據庫登錄密碼: db2password,用戶信息表為: userTable,該表包含用戶名和密碼的兩個數據項分別為 userName 和 password。
1. 配置 DataStore
打開文件 %CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,添加一個新的 bean 標簽,對于 DB2,內容如清單 4 所示:
清單 4. 配置 DataStore
<bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>com.ibm.db2.jcc.DB2Driver</value> </property> <property name="url"> <value>jdbc:db2://9.125.65.134:50000/CASTest</value> </property> <property name="username"> <value>db2user</value> </property> <property name="password"> <value>db2password</value> </property> </bean> |
其中 id 屬性為該 DataStore 的標識,在后面配置 AuthenticationHandler 會被引用,另外,需要提供 DataStore 所必需的數據庫驅動程序、連接地址、數據庫登錄用戶名以及登錄密碼。
2. 配置 AuthenticationHandler
在 cas-server-support-jdbc-3.1.1.jar 包中,提供了 3 個基于 JDBC 的 AuthenticationHandler,分別為 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所給的用戶名和密碼去建立數據庫連接,根據連接建立是否成功來判斷驗證成功與否;QueryDatabaseAuthenticationHandler 通過配置一個 SQL 語句查出密碼,與所給密碼匹配;SearchModeSearchDatabaseAuthenticationHandler 通過配置存放用戶驗證信息的表、用戶名字段和密碼字段,構造查詢語句來驗證。
使用哪個 AuthenticationHandler,需要在 deployerConfigContext.xml 中設置,默認情況下,CAS 使用一個簡單的 username=password 的 AuthenticationHandler,在文件中可以找到如下一行:<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword
AuthenticationHandler" />,我們可以將其注釋掉,換成我們希望的一個 AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 可以分別選取清單 5 或清單 6 的配置。
清單 5. 使用 QueryDatabaseAuthenticationHandler
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="dataSource" ref=" casDataSource " /> <property name="sql" value="select password from userTable where lower(userName) = lower(?)" /> </bean> |
清單 6. 使用 SearchModeSearchDatabaseAuthenticationHandler
<bean id="SearchModeSearchDatabaseAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler" abstract="false" singleton="true" lazy-init="default" autowire="default" dependency-check="default"> <property name="tableUsers"> <value>userTable</value> </property> <property name="fieldUser"> <value>userName</value> </property> <property name="fieldPassword"> <value>password</value> </property> <property name="dataSource" ref=" casDataSource " /> </bean> |
另外,由于存放在數據庫中的密碼通常是加密過的,所以 AuthenticationHandler 在匹配時需要知道使用的加密方法,在 deployerConfigContext.xml 文件中我們可以為具體的 AuthenticationHandler 類配置一個 property,指定加密器類,比如對于 QueryDatabaseAuthenticationHandler,可以修改如清單7所示:
清單 7. 添加 passwordEncoder
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="dataSource" ref=" casDataSource " /> <property name="sql" value="select password from userTable where lower(userName) = lower(?)" /> <property name="passwordEncoder" ref="myPasswordEncoder"/> </bean> |
其中 myPasswordEncoder 是對清單 8 中設置的實際加密器類的引用:
清單 8. 指定具體加密器類
<bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/> |
這里 MyPasswordEncoder 是根據實際情況自己定義的加密器,實現 PasswordEncoder 接口及其 encode() 方法。
3. 部署依賴包
在以上配置完成以后,需要拷貝幾個依賴的包到 cas 應用下,包括:
- 將 cas-server-support-jdbc-3.1.1.jar 拷貝到 %CATALINA_HOME%/webapps/cas/ WEB-INF/lib 目錄。
- 數據庫驅動,由于這里使用 DB2,將 %DB2_HOME%/java 目錄下的 db2java.zip (更名為 db2java.jar), db2jcc.jar, db2jcc_license_cu.jar 拷貝到 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目錄。對于其他數據庫,同樣將相應數據庫驅動程序拷貝到該目錄。
- DataStore 依賴于 commons-collections-3.2.jar, commons-dbcp-1.2.1.jar, commons-pool-1.3.jar,需要到 apache 網站的 Commons 項目下載以上 3 個包放進 %CATALINA_HOME%/webapps/cas/WEB-INF/lib 目錄。
CAS 提供了 2 套默認的頁面,分別為“ default ”和“ simple ”,分別在目錄“ cas/WEB-INF/view/jsp/default ”和“ cas/WEB-INF/view/jsp/simple ”下。其中 default 是一個稍微復雜一些的頁面,使用 CSS,而 simple 則是能讓 CAS 正常工作的最簡化的頁面。
在部署 CAS 之前,我們可能需要定制一套新的 CAS Server 頁面,添加一些個性化的內容。最簡單的方法就是拷貝一份 default 或 simple 文件到“ cas/WEB-INF/view/jsp ”目錄下,比如命名為 newUI,接下來是實現和修改必要的頁面,有 4 個頁面是必須的:
- casConfirmView.jsp: 當用戶選擇了“ warn ”時會看到的確認界面
- casGenericSuccess.jsp: 在用戶成功通過認證而沒有目的Service時會看到的界面
- casLoginView.jsp: 當需要用戶提供認證信息時會出現的界面
- casLogoutView.jsp: 當用戶結束 CAS 單點登錄系統會話時出現的界面
CAS 的頁面采用 Spring 框架編寫,對于不熟悉 Spring 的使用者,在修改之前需要熟悉該框架。
頁面定制完過后,還需要做一些配置從而讓 CAS 找到新的頁面,拷貝“ cas/WEB-INF/classes/default_views.properties ”,重命名為“ cas/WEB-INF/classes/ newUI_views.properties ”,并修改其中所有的值到相應新頁面。最后是更新“ cas/WEB-INF/cas-servlet.xml ”文件中的 viewResolver,將其修改為如清單 9 中的內容。
清單 9. 指定 CAS 頁面
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver" p:order="0"> <property name="basenames"> <list> <value>${cas.viewResolver.basename}</value> <value> newUI_views</value> </list> </property> </bean> |
![]() ![]() |
![]()
|
單點登錄的目的是為了讓多個相關聯的應用使用相同的登錄過程,本文在講解過程中構造 2個簡單的應用,分別以 casTest1 和 casTest2 來作為示例,它們均只有一個頁面,顯示歡迎信息和當前登錄用戶名。這 2 個應用使用同一套登錄信息,并且只有登錄過的用戶才能訪問,通過本文的配置,實現單點登錄,即只需登錄一次就可以訪問這兩個應用。
假設 CAS Server 單獨部署在一臺機器 A,而客戶端應用部署在機器 B 上,由于客戶端應用與 CAS Server 的通信采用 SSL,因此,需要在 A 與 B 的 JRE 之間建立信任關系。
首先與 A 機器一樣,要生成 B 機器上的證書,配置 Tomcat 的 SSL 協議。其次,下載http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,運行“ java InstallCert compA:8443 ”命令,并且在接下來出現的詢問中輸入 1。這樣,就將 A 添加到了 B 的 trust store 中。如果多個客戶端應用分別部署在不同機器上,那么每個機器都需要與 CAS Server 所在機器建立信任關系。
準備好應用 casTest1 和 casTest2 過后,分別部署在 B 和 C 機器上,由于 casTest1 和casTest2,B 和 C 完全等同,我們以 casTest1 在 B 機器上的配置做介紹,假設 A 和 B 的域名分別為 domainA 和 domainB。
將 cas-client-java-2.1.1.zip 改名為 cas-client-java-2.1.1.jar 并拷貝到 casTest1/WEB-INF/lib目錄下,修改 web.xml 文件,添加 CAS Filter,如清單 10 所示:
清單 10. 添加 CAS Filter
<web-app> ... <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://domainA:8443/cas/login</param-value> </init-param> <init-param> <param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name> <param-value>https://domainA:8443/cas/serviceValidate</param-value> </init-param> <init-param> <param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name> <param-value>domainB:8080</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Filter</filter-name> <url-pattern>/protected-pattern/*</url-pattern> </filter-mapping> ... </web-app> |
對于所有訪問滿足 casTest1/protected-pattern/ 路徑的資源時,都要求到 CAS Server 登錄,如果需要整個 casTest1 均受保護,可以將 url-pattern 指定為“/*”。
從清單 10 可以看到,我們可以為 CASFilter 指定一些參數,并且有些是必須的,表格 1 和表格 2 中分別是必需和可選的參數:
表格 1. CASFilter 必需的參數
參數名 | 作用 |
edu.yale.its.tp.cas.client.filter.loginUrl | 指定 CAS 提供登錄頁面的 URL |
edu.yale.its.tp.cas.client.filter.validateUrl | 指定 CAS 提供 service ticket 或 proxy ticket 驗證服務的 URL |
edu.yale.its.tp.cas.client.filter.serverName | 指定客戶端的域名和端口,是指客戶端應用所在機器而不是 CAS Server 所在機器,該參數或 serviceUrl 至少有一個必須指定 |
edu.yale.its.tp.cas.client.filter.serviceUrl | 該參數指定過后將覆蓋 serverName 參數,成為登錄成功過后重定向的目的地址 |
表格 2. CASFilter 可選參數
參數名 | 作用 |
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl | 用于當前應用需要作為其他服務的代理(proxy)時獲取 Proxy Granting Ticket 的地址 |
edu.yale.its.tp.cas.client.filter.authorizedProxy | 用于允許當前應用從代理處獲取 proxy tickets,該參數接受以空格分隔開的多個 proxy URLs,但實際使用只需要一個成功即可。當指定該參數過后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate |
edu.yale.its.tp.cas.client.filter.renew | 如果指定為 true,那么受保護的資源每次被訪問時均要求用戶重新進行驗證,而不管之前是否已經通過 |
edu.yale.its.tp.cas.client.filter.wrapRequest | 如果指定為 true,那么 CASFilter 將重新包裝 HttpRequest,并且使 getRemoteUser() 方法返回當前登錄用戶的用戶名 |
edu.yale.its.tp.cas.client.filter.gateway | 指定 gateway 屬性 |
CAS 在登錄成功過后,會給瀏覽器回傳 Cookie,設置新的到的 Service Ticket。但客戶端應用擁有各自的 Session,我們要怎么在各個應用中獲取當前登錄用戶的用戶名呢?CAS Client 的 Filter 已經做好了處理,在登錄成功后,就可以直接從 Session 的屬性中獲取,如清單 11 所示:
清單 11. 在 Java 中通過 Session 獲取登錄用戶名
// 以下兩者都可以 session.getAttribute(CASFilter.CAS_FILTER_USER); session.getAttribute("edu.yale.its.tp.cas.client.filter.user"); |
在 JSTL 中獲取用戶名的方法如清單 12 所示:
清單 12. 通過 JSTL 獲取登錄用戶名
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/> |
另外,CAS 提供了一個 CASFilterRequestWrapper 類,該類繼承自HttpServletRequestWrapper,主要是重寫了 getRemoteUser() 方法,只要在前面配置 CASFilter 的時候為其設置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”參數為 true,就可以通過 getRemoteUser() 方法來獲取登錄用戶名,具體方法如清單 13 所示:
清單 13. 通過 CASFilterRequestWrapper 獲取登錄用戶名
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request); out.println("The logon user:" + reqWrapper.getRemoteUser()); |
![]() ![]() |
![]()
|
在 casTest1 和 casTest2 中,都有一個簡單 Servlet 作為歡迎頁面 WelcomPage,且該頁面必須登錄過后才能訪問,頁面代碼如清單 14 所示:
清單 14. WelcomePage 頁面代碼
public class WelcomePage extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Welcome to casTest2 sample System!</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Welcome to casTest1 sample System!</h1>"); CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request); out.println("<p>The logon user:" + reqWrapper.getRemoteUser() + "</p>"); HttpSession session=request.getSession(); out.println("<p>The logon user:" + session.getAttribute(CASFilter.CAS_FILTER_USER) + "</p>"); out.println("<p>The logon user:" + session.getAttribute("edu.yale.its.tp.cas.client.filter.user") + "</p>"); out.println("</body>"); out.println("</html>"); } } |
在上面所有配置結束過后,分別在 A, B, C上啟動 cas, casTest1 和 casTest2,按照下面步驟來訪問 casTest1 和 casTest2:
- 打開瀏覽器,訪問 http://domainB:8080/casTest1/WelcomePage ,瀏覽器會彈出安全提示,接受后即轉到 CAS 的登錄頁面,如圖 2 所示:
圖 2. CAS 登錄頁面

- 登錄成功后,再重定向到 casTest1 的 WelcomePage 頁面,如圖 所示:
圖 3. 登錄后訪問 casTest1 的效果

可以看到圖 中地址欄里的地址多出了一個 ticket 參數,這就是 CAS 分配給當前應用的 ST(Service Ticket)。
- 再在同一個瀏覽器的地址欄中輸入 http://domainC:8080/casTest2/WelcomePage ,系統不再提示用戶登錄,而直接出現如圖 4 所示的頁面,并且顯示在 casTest1 中已經登錄過的用戶。
圖 4. 在 casTest1 中登錄過后訪問 casTest2 的效果

- 重新打開一個瀏覽器窗口,先輸入 http://domainC:8080/casTest2/WelcomePage ,系統要求登錄,在登錄成功過后,正確顯示 casTest2 的頁面。之后再在地址欄重新輸入 http://domainB:8080/casTest1/WelcomePage ,會直接顯示 casTest1 的頁面而無需再次登錄。
http://www.ibm.com/developerworks/cn/opensource/os-cn-cas/index.html
淺析Web單點登錄和安全斷言標記語言技術
SAML已經被結構化信息標準促進組織(OASIS)批準為Web 單點登錄的執(zhí)行標準。SAML連同Web單點登錄共同構成了現代網絡環(huán)境中的必備條件。
美國在線和eBay是兩個知名網站,擁有大量的用戶。通過分析,這兩個網站發(fā)現他們有30%的用戶是重合的,也就是說,這些用戶在兩個網站都有用戶名和密碼。因此,他們認為有必要找到一種方法,讓這30%的用戶在登錄其中一個網站時,其身份能夠自動被另一網站識別,并可以訪問另一網站的資源。最終,他們采用了Web單點登錄解決方案。
單點登錄:大勢所趨
在大多數計算機連入網絡之前,各個系統中像身份驗證和授權這類安全服務的實現完全是獨立的。因此,執(zhí)行身份驗證所需的全部代碼,以及密鑰、口令,供授權決策所用的用戶信息,以及授權策略本身均存放于使用這些信息的系統上。最初,系統連接到網絡上時情況變化不大。每個系統都是一個孤島,各系統都要求用戶擁有一個賬戶才能訪問該系統。
這種方法有許多明顯的缺點。舉例來說,設置多個賬戶,每個賬戶有一個密碼、組或其他屬性,這對用戶和管理員來說非常不方便。如果一個用戶的職責發(fā)生變更而修改其賬戶屬性,或有人離開組織時刪除其賬戶,管理員要浪費大量時間。如果出現更強大的認證方法,各系統還必須單獨地去升級。
隨著互聯網的出現,多臺機器作為一個Web站點的主機成為一種普遍現象。但僅僅因為用戶要使用不同的機器處理不同的請求而強迫他們多次在網絡上進行登錄,顯然是令人無法接受的。同樣,門戶也不能要求使用者每訪問不同的應用程序就重新登錄。于是單點登錄(Single Sign-on,SSO)出現了。單點登錄最初被視為一種提高生產力的奢侈品,而現在已成為一種必需品。當前有越來越多的身份與訪問控制管理(IAM)需求來自Web應用和Web Services,從IDC的市場預測也可以看出,年復合增長率CAGR最高的兩個子方向之一就是Web單點登錄,達到了20.8%。
單點登錄是一種用于方便用戶訪問網絡的技術。無論多么復雜的網絡結構,用戶只需在登錄時進行一次注冊,即可獲得訪問系統和應用軟件的授權,以后便可以在網絡中自由穿梭,不必多次輸入用戶名和口令來確定身份。在此條件下,管理員無需修改或干涉用戶登錄就能方便地實施希望得到的安全控制。
實現單點登錄后,認證網絡用戶能夠利用連貫且安全的身份識別信息交換機制,在其自身運行環(huán)境或業(yè)務合作伙伴運行環(huán)境下,便捷地在兩個應用程序中移動,而無需重新認證。
SAML:一統Web SSO江湖
當今,越來越多的系統通過Web服務、門戶和集成化應用程序彼此鏈接,對于保證信息安全交換標準的需求也隨之日益增多。安全斷言標記語言(Security Assertion Markup Language,SAML)提供了一個健壯且可擴展的數據格式集,在各種環(huán)境下交換數據和身份識別信息。SAML的出現大大簡化了Web單點登錄,并被結構化信息標準促進組織(OASIS)批準為Web SSO的執(zhí)行標準。這里的一個關鍵概念是身份聯邦,它可滿足SAML的定義,也就是說可使用獨立、受管理的多個信息來源中的信息。SAML連同Web單點登錄共同構成現代網絡環(huán)境中的必備條件。
身份聯邦是企業(yè)身份和訪問管理戰(zhàn)略的關鍵組成部分,提供了多項業(yè)務優(yōu)勢。對于企業(yè)而言,身份聯邦增強了與業(yè)務合作伙伴進行合作、管理供應鏈、為客戶提供新型創(chuàng)收服務的能力,并且保護了企業(yè)資源,降低了成本。對于最終用戶而言,身份聯邦使網站訪問更加便捷,提高了工作效率;提供了更廣泛的訪問信息與服務;有效保護了個人信息。
另外,隨著互聯網規(guī)模的不斷擴大,把一個用戶的所有信息全部收集到一個地方,既不可能也不需要。不同個人和組織在與不同的對象打交道時會使用不同類型的信息,例如醫(yī)生掌管病歷卡、會計師保存財政和稅款記錄等等。經常性地將此信息移動到一個地點,只能使保持數據的準確性和及時更新更加困難。同時,移動信息還會增加數據在傳輸中丟失和被竊的可能性。
盡管如此,為進行身份驗證和授權,還是必須在網絡上保留許多類型的信息。這也正是身份聯邦的目的。出于授權等目的,身份聯邦將來自多個數據源的同一用戶數據綜合在一起。不同的組織可能希望使用不同的產品去管理其身份數據,那么自然就需要制訂一個在網絡上傳送這些數據(從數據當前所在的地方,到現在正需要數據的地方)的標準。雖然許多產品提供Web單點登錄,同樣需要一種標準使這種跨不同產品的傳送成為可能,這就是SAML關注的領域。
舉一個簡單的例子:用戶在一個站點上取得認證授權,當用戶需要訪問另一個相關站點的資源時,目的站點(保護資源的持有者)能夠使用SAML從源站點調取用戶的證書信息。此時SAML對信息交換的處理發(fā)生在后臺,因此用戶的資源實際上被不同的安全系統進行了定位。
SAML是什么
Web安全方面最具挑戰(zhàn)性的一個問題是維持一次無縫操作和安全環(huán)境時, 使各不相同的安全系統達到一體化。比如在電子商務活動過程中,經常需要通過網絡來交換機密的資料或數據,因此,對于安全功能的要求十分嚴格。
OASIS建立的安全標準—SAML是基于XML(可擴展標記語言)、面向Web服務的架構。SAML通過互聯網對不同安全系統的信息交換進行處理。
SAML是一種語言,進行單一的XML描述,允許不同安全系統產生的信息進行交換。通常來說,一個企業(yè)在物理或邏輯的范圍已經界定了企業(yè)的IT安全;然而,由于在線合作需要共享更可靠的安全服務環(huán)境,因此IT安全越來越成為人們關注的重點問題。
SAML正是為解決網絡安全性問題而發(fā)揮其作用。SAML在傳統意義上的安全界定與商務站點之間建立了一種安全信息的交換渠道。SAML作為安全信息交換的“中間人”,促使一個站點上的交易業(yè)務能夠在另一個信任的站點上得到處理完成。由此可見,實現交易雙方商業(yè)協議或合作的一個先決條件,是要求使用SAML作為共享安全架構的一部分。
SAML在標準行業(yè)傳輸協議環(huán)境里工作,例如HTTP、SMTP和FTP;同時也服務于各種各樣的XML文件交換框架,例如SOAP和BizTalk。SAML具備的一個最突出的好處,是使用戶能夠通過互聯網進行安全證書移動。也就是說,使用SAML標準作為安全認證和共享資料的中間語言,能夠在多個站點之間實現單點登錄。
SAML是一種基于XML語言用于傳輸認證及授權信息的框架,以與主體相關的斷言形式表達。在這里,主體是一個實體(人或計算機),這個實體在某個安全域中擁有一個特定身份,斷言可傳遞主體執(zhí)行的認證信息、屬性信息及關于是否允許主體訪問其資源的授權決定。針對以上不同目的,SAML提供以下幾種不同類型的安全斷言:
● 認證斷言(Authentication Assertion):認證斷言用來聲稱消息發(fā)布者已經認證特定的主體。
● 屬性斷言(Attribute Assertion):屬性斷言聲稱特定主體具有特定的屬性。屬性可通過URI(統一資源標識)或用來定義結構化屬性的一種擴展模式進行詳細說明。
● 決定斷言(Decision Assertion):一個決定斷言報告了一個具體授權請求的結果。
● 授權斷言(Authorization Assertion):授權斷言聲稱一個主體被給予訪問一個或多個資源的特別許可。
SAML斷言以XML結構描述且具有嵌套結構,由此一個斷言可能包括幾個關于認證、授權和屬性的不同內在斷言(包括認證聲明的斷言僅僅描述那些先前發(fā)生的認證行為)。
主流標準:SAML 2.0
在2005年底,隨著監(jiān)控、移動設備、寬帶業(yè)務以及應用安全領域的四家主要廠商通過了最后一回合的聯邦身份互操作性測試,自由聯盟(Liberty Alliance)公布了SAML 2.0。
SAML 2.0版在2005年3月剛剛被OASIS批準。Liberty Alliance的目的是讓盡可能多的廠商把SAML加入到他們的產品線中。
通過互操作性測試的有四種產品,分別是IBM的Tivoli Federated Identity Manager、NEC 的Mobile Internet Platform、NTT Communication的I-dLive 寬帶與網絡服務身份聯邦工具以及RSA Security公司的Federated Identity Manager。為了證明其互操作性,每個產品必須能夠與至少兩個廠商共享一個給定的SAML文件。
Oracle和Sun公司在2005年7月已經通過了SAML2.0互操作性測試?,F在,Liberty宣稱已經有超過70種產品被授予了SAML證書。
Liberty Alliance Conformance Expert Group的主席Roger Sulliva說:“互操作性是必須的。聯邦的本質就是需要至少兩個公司之間能夠進行互操作。只有當你可以在網絡中的任何訪問點都能做到即插即用,這個標準才能起作用。”
Sullivan表示,“我們的目標是為聯邦制定出事實上的標準。”SAML并非惟一想達到這個目標的標準。OASIS最近在組建一個旨在建立一套新的安全標準的委員會。而尚在開發(fā)中的WS-Federation規(guī)格說明書卻沒有加入進來,人們預計它明年會跟進。
有些人對WS-Federation的出現是否會在與SAML的結合部引起標準之間的爭斗存在疑慮,Sullivan認為那將是兩敗俱傷的結局。他說:“假如標準之間不融合,那么廠商就不得不支持所有的標準。”
Microsoft和IBM在建立WS-Federation方面很積極。Microsoft的產品現在還沒有獲得SAML證書,而IBM的Tivoli產品現在已經支持SAML以及WS-Federation。
隨著新的Web 服務安全標準被批準,Sullivan相信SAML會被采納、贊同并獲得優(yōu)勢地位。
HP公司軟件技術顧問王志剛在接受記者采訪時表示,目前業(yè)界基本上是兩大陣營,OASIS/Liberty與WS-*,即SAML2.0與WS-Federation,二者其實有些地方是重復的,未來統一的單一標準究竟會以哪個為基礎,還沒有定論,或許會建立在二者之上。
以前,聯邦身份一直受到標準過多問題的困擾。5個不兼容的協議(OASIS SAML1.0和1.1、自由聯盟ID-FF 1.1和1.2以及Shibboleth)給企業(yè)和消費者的應用帶來了麻煩,延緩了發(fā)展速度,增加了聯邦身份部署的費用。尋求部署聯邦身份的機構必須與每一位聯邦合作伙伴協商選擇協議。很多機構必須通過協議映射和轉換技術來支持多個協議,而這些技術造成關鍵特性或功能的支持空隙。
SAML 2.0消除了阻礙進一步采用聯邦身份的最大障礙—多協議復雜性,因而大大改變了聯邦身份的局面。SAML 2.0將來自每一個前任協議的各種關鍵使用情況和特性融入到一項標準中。由于SAML 2.0代表5個前任協議中所有功能性的集合,因此它將淘汰以前的協議。
SAML 2.0說明實現聯邦的兩個角色。服務提供者是為用戶提供應用或資源的實體,而身份提供商負責認證用戶。服務提供者和身份提供者交換信息,以實現單一登錄和退出。這些信息交換可以由身份提供者或服務提供者發(fā)起。
在進行單一登錄時,身份提供者負責創(chuàng)建包含用戶身份的SAML斷言,然后安全地將這個斷言發(fā)送給服務提供者。服務提供者負責在讓用戶訪問應用之前驗證SAML斷言的有效性。
優(yōu)勢明顯
利用SAML,網絡服務不需要借助安全認證機構就可驗證令牌的有效性,不僅簡化了單點登錄步驟,還帶來了許多傳統單點登錄方式所不具備的優(yōu)點:
1.SAML為認證聲明和認證屬性建立了一個數據格式,其參數取決于安全服務產生的基于政策的認證結果。使用SAML標準作為安全認證和共享資料的中間語言,能夠在多個站點之間實現單點登錄。
2. SAML針對不同的安全系統提供了一個共有的框架,允許企業(yè)及其供應商、客戶與合作伙伴進行安全的認證、授權和基本信息交換。由于SAML是通過XML對現有的安全模式進行描述,因此它是一個中立的平臺并且不需要依賴于供應商的基礎結構。
3. SAML的消息格式能夠從一個源站點(站點起到SAML認證管理機構的作用)將斷言發(fā)送給一個接受者。使電子商務合作中的事務處理速度得到加快,并且使認證環(huán)境的復雜性得到全面的簡化。
編看編想
關于單點登錄的幾點認識
單點登錄是個熱門概念,所有身份與訪問控制管理(IAM)產品中都有單點登錄的身影。但是,眾多的單點登錄概念和實現技術讓我仿佛墜入了五里霧中,在撰寫本文之初,我既分不清單點登錄之間的區(qū)別,也不明白單點登錄和SAML之間的關系。還好,通過向專業(yè)人士請教,我大概明白了一二。
根據我的理解,首先,單點登錄可以分為企業(yè)內部的單點登錄和Web單點登錄兩大類。如果企業(yè)內的應用都是B/S架構,或者企業(yè)內有C/S架構、B/S架構和Telnet應用,想實現單點登錄,就需要采用企業(yè)內部單點登錄解決方案。如果要在不同域名或不同企業(yè)間,或者同一企業(yè)的不同分支機構之間實現單點登錄,Web單點登錄則是更好的選擇。
其次,Web單點登錄已經有了統一的標準,就是SAML 2.0。而企業(yè)內部的單點登錄則沒有任何標準,由于各企業(yè)內部應用的復雜性和獨特性,每個廠商都在按照自己的設計思路和用戶需求來提供解決方案。
第三,國內的Web單點登錄應用遠遠落后于國外。據RSA公司的工程師陳海林介紹,在國內,企業(yè)內部單點登錄的應用較多,而Web單點登錄才剛剛起步,這和國內的互聯網發(fā)展進程密切相關。
本博客為學習交流用,凡未注明引用的均為本人作品,轉載請注明出處,如有版權問題請及時通知。由于博客時間倉促,錯誤之處敬請諒解,有任何意見可給我留言,愿共同學習進步。