單點(diǎn)登錄(Single Sign On , 簡(jiǎn)稱(chēng) SSO )是目前比較流行的服務(wù)于企業(yè)業(yè)務(wù)整合的解決方案之一, SSO 使得在多個(gè)應(yīng)用系統(tǒng)中,用戶(hù)只需要登錄一次就可以訪問(wèn)所有相互信任的應(yīng)用系統(tǒng)。CAS(Central Authentication Service)是一款不錯(cuò)的針對(duì) Web 應(yīng)用的單點(diǎn)登錄框架,本文介紹了 CAS 的原理、協(xié)議、在 Tomcat 中的配置和使用,對(duì)于采用 CAS 實(shí)現(xiàn)輕量級(jí)單點(diǎn)登錄解決方案的入門(mén)讀者具有一定指導(dǎo)作用。
CAS 介紹
CAS 是 Yale 大學(xué)發(fā)起的一個(gè)開(kāi)源項(xiàng)目,旨在為 Web 應(yīng)用系統(tǒng)提供一種可靠的單點(diǎn)登錄方法,CAS 在 2004 年 12 月正式成為 JA-SIG 的一個(gè)項(xiàng)目。CAS 具有以下特點(diǎn):
CAS 原理和協(xié)議
從結(jié)構(gòu)上看,CAS 包含兩個(gè)部分: CAS Server 和 CAS Client。CAS Server 需要獨(dú)立部署,主要負(fù)責(zé)對(duì)用戶(hù)的認(rèn)證工作;CAS Client 負(fù)責(zé)處理對(duì)客戶(hù)端受保護(hù)資源的訪問(wèn)請(qǐng)求,需要登錄時(shí),重定向到 CAS Server。圖1 是 CAS 最基本的協(xié)議過(guò)程:
CAS Client 與受保護(hù)的客戶(hù)端應(yīng)用部署在一起,以 Filter 方式保護(hù)受保護(hù)的資源。對(duì)于訪問(wèn)受保護(hù)資源的每個(gè) Web 請(qǐng)求,CAS Client 會(huì)分析該請(qǐng)求的 Http 請(qǐng)求中是否包含 Service Ticket,如果沒(méi)有,則說(shuō)明當(dāng)前用戶(hù)尚未登錄,于是將請(qǐng)求重定向到指定好的 CAS Server 登錄地址,并傳遞 Service (也就是要訪問(wèn)的目的資源地址),以便登錄成功過(guò)后轉(zhuǎn)回該地址。用戶(hù)在第 3 步中輸入認(rèn)證信息,如果登錄成功,CAS Server 隨機(jī)產(chǎn)生一個(gè)相當(dāng)長(zhǎng)度、唯一、不可偽造的 Service Ticket,并緩存以待將來(lái)驗(yàn)證,之后系統(tǒng)自動(dòng)重定向到 Service 所在地址,并為客戶(hù)端瀏覽器設(shè)置一個(gè) Ticket Granted Cookie(TGC),CAS Client 在拿到 Service 和新產(chǎn)生的 Ticket 過(guò)后,在第 5,6 步中與 CAS Server 進(jìn)行身份合適,以確保 Service Ticket 的合法性。
在該協(xié)議中,所有與 CAS 的交互均采用 SSL 協(xié)議,確保,ST 和 TGC 的安全性。協(xié)議工作過(guò)程中會(huì)有 2 次重定向的過(guò)程,但是 CAS Client 與 CAS Server 之間進(jìn)行 Ticket 驗(yàn)證的過(guò)程對(duì)于用戶(hù)是透明的。
另外,CAS 協(xié)議中還提供了 Proxy (代理)模式,以適應(yīng)更加高級(jí)、復(fù)雜的應(yīng)用場(chǎng)景,具體介紹可以參考 CAS 官方網(wǎng)站上的相關(guān)文檔。
準(zhǔn)備工作
本文中的例子以 tomcat5.5 為例進(jìn)行講解,下載地址:
http://tomcat.apache.org/download-55.cgi
到 CAS 官方網(wǎng)站下載 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
CAS Server 是一套基于 Java 實(shí)現(xiàn)的服務(wù),該服務(wù)以一個(gè) Java Web Application 單獨(dú)部署在與 servlet2.3 兼容的 Web 服務(wù)器上,另外,由于 Client 與 CAS Server 之間的交互采用 Https 協(xié)議,因此部署 CAS Server 的服務(wù)器還需要支持 SSL 協(xié)議。當(dāng) SSL 配置成功過(guò)后,像普通 Web 應(yīng)用一樣將 CAS Server 部署在服務(wù)器上就能正常運(yùn)行了,不過(guò),在真正使用之前,還需要擴(kuò)展驗(yàn)證用戶(hù)的接口。
在 Tomcat 上部署一個(gè)完整的 CAS Server 主要按照以下幾個(gè)步驟:
配置 Tomcat 使用 Https 協(xié)議
如果希望 Tomcat 支持 Https,主要的工作是配置 SSL 協(xié)議,其配置過(guò)程和配置方法可以參考 Tomcat 的相關(guān)文檔。不過(guò)在生成證書(shū)的過(guò)程中,會(huì)有需要用到主機(jī)名的地方,CAS 建議不要使用 IP 地址,而要使用機(jī)器名或域名。
部署 CAS Server
CAS Server 是一個(gè) Web 應(yīng)用包,將前面下載的 cas-server-3.1.1-release.zip 解開(kāi),把其中的 cas-server-webapp-3.1.1.war 拷貝到 tomcat的 webapps 目錄,并更名為 cas.war。由于前面已配置好 tomcat 的 https 協(xié)議,可以重新啟動(dòng) tomcat,然后訪問(wèn):https://localhost:8443/cas ,如果能出現(xiàn)正常的 CAS 登錄頁(yè)面,則說(shuō)明 CAS Server 已經(jīng)部署成功。
雖然 CAS Server 已經(jīng)部署成功,但這只是一個(gè)缺省的實(shí)現(xiàn),在實(shí)際使用的時(shí)候,還需要根據(jù)實(shí)際概況做擴(kuò)展和定制,最主要的是擴(kuò)展認(rèn)證 (Authentication) 接口和 CAS Server 的界面。
擴(kuò)展認(rèn)證接口
CAS Server 負(fù)責(zé)完成對(duì)用戶(hù)的認(rèn)證工作,它會(huì)處理登錄時(shí)的用戶(hù)憑證 (Credentials) 信息,用戶(hù)名/密碼對(duì)是最常見(jiàn)的憑證信息。CAS Server 可能需要到數(shù)據(jù)庫(kù)檢索一條用戶(hù)賬號(hào)信息,也可能在 XML 文件中檢索用戶(hù)名/密碼,還可能通過(guò) LDAP Server 獲取等,在這種情況下,CAS 提供了一種靈活但統(tǒng)一的接口和實(shí)現(xiàn)分離的方式,實(shí)際使用中 CAS 采用哪種方式認(rèn)證是與 CAS 的基本協(xié)議分離開(kāi)的,用戶(hù)可以根據(jù)認(rèn)證的接口去定制和擴(kuò)展。
擴(kuò)展 AuthenticationHandler
CAS 提供擴(kuò)展認(rèn)證的核心是 AuthenticationHandler 接口,該接口定義如清單 1 下:
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 個(gè)需要實(shí)現(xiàn)的方法,supports ()方法用于檢查所給的包含認(rèn)證信息的Credentials 是否受當(dāng)前 AuthenticationHandler 支持;而 authenticate() 方法則擔(dān)當(dāng)驗(yàn)證認(rèn)證信息的任務(wù),這也是需要擴(kuò)展的主要方法,根據(jù)情況與存儲(chǔ)合法認(rèn)證信息的介質(zhì)進(jìn)行交互,返回 boolean 類(lèi)型的值,true 表示驗(yàn)證通過(guò),false 表示驗(yàn)證失敗。
CAS3中還提供了對(duì)AuthenticationHandler 接口的一些抽象實(shí)現(xiàn),比如,可能需要在執(zhí)行authenticate() 方法前后執(zhí)行某些其他操作,那么可以讓自己的認(rèn)證類(lèi)擴(kuò)展自清單 2 中的抽象類(lèi):
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 類(lèi)新定義了 preAuthenticate() 方法和 postAuthenticate() 方法,而實(shí)際的認(rèn)證工作交由 doAuthentication() 方法來(lái)執(zhí)行。因此,如果需要在認(rèn)證前后執(zhí)行一些額外的操作,可以分別擴(kuò)展 preAuthenticate()和 ppstAuthenticate() 方法,而 doAuthentication() 取代 authenticate() 成為了子類(lèi)必須要實(shí)現(xiàn)的方法。
由于實(shí)際運(yùn)用中,最常用的是用戶(hù)名和密碼方式的認(rèn)證,CAS3 提供了針對(duì)該方式的實(shí)現(xiàn),如清單 3 所示:
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; } ... } |
基于用戶(hù)名密碼的認(rèn)證方式可直接擴(kuò)展自 AbstractUsernamePasswordAuthenticationHandler,驗(yàn)證用戶(hù)名密碼的具體操作通過(guò)實(shí)現(xiàn) authenticateUsernamePasswordInternal() 方法達(dá)到,另外,通常情況下密碼會(huì)是加密過(guò)的,setPasswordEncoder() 方法就是用于指定適當(dāng)?shù)募用芷鳌?/p>
從以上清單中可以看到,doAuthentication() 方法的參數(shù)是 Credentials 類(lèi)型,這是包含用戶(hù)認(rèn)證信息的一個(gè)接口,對(duì)于用戶(hù)名密碼類(lèi)型的認(rèn)證信息,可以直接使用 UsernamePasswordCredentials,如果需要擴(kuò)展其他類(lèi)型的認(rèn)證信息,需要實(shí)現(xiàn)Credentials接口,并且實(shí)現(xiàn)相應(yīng)的 CredentialsToPrincipalResolver 接口,其具體方法可以借鑒 UsernamePasswordCredentials 和 UsernamePasswordCredentialsToPrincipalResolver。
JDBC 認(rèn)證方法
用戶(hù)的認(rèn)證信息通常保存在數(shù)據(jù)庫(kù)中,因此本文就選用這種情況來(lái)介紹。將前面下載的 cas-server-3.1.1-release.zip 包解開(kāi)后,在 modules 目錄下可以找到包 cas-server-support-jdbc-3.1.1.jar,其提供了通過(guò) JDBC 連接數(shù)據(jù)庫(kù)進(jìn)行驗(yàn)證的缺省實(shí)現(xiàn),基于該包的支持,我們只需要做一些配置工作即可實(shí)現(xiàn) JDBC 認(rèn)證。
JDBC 認(rèn)證方法支持多種數(shù)據(jù)庫(kù),DB2, Oracle, MySql, Microsoft SQL Server 等均可,這里以 DB2 作為例子介紹。并且假設(shè)DB2數(shù)據(jù)庫(kù)名: CASTest,數(shù)據(jù)庫(kù)登錄用戶(hù)名: db2user,數(shù)據(jù)庫(kù)登錄密碼: db2password,用戶(hù)信息表為: userTable,該表包含用戶(hù)名和密碼的兩個(gè)數(shù)據(jù)項(xiàng)分別為 userName 和 password。
1. 配置 DataStore
打開(kāi)文件 %CATALINA_HOME%/webapps/cas/WEB-INF/deployerConfigContext.xml,添加一個(gè)新的 bean 標(biāo)簽,對(duì)于 DB2,內(nèi)容如清單 4 所示:
<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 的標(biāo)識(shí),在后面配置 AuthenticationHandler 會(huì)被引用,另外,需要提供 DataStore 所必需的數(shù)據(jù)庫(kù)驅(qū)動(dòng)程序、連接地址、數(shù)據(jù)庫(kù)登錄用戶(hù)名以及登錄密碼。
2. 配置 AuthenticationHandler
在 cas-server-support-jdbc-3.1.1.jar 包中,提供了 3 個(gè)基于 JDBC 的 AuthenticationHandler,分別為 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler。其中 BindModeSearchDatabaseAuthenticationHandler 是用所給的用戶(hù)名和密碼去建立數(shù)據(jù)庫(kù)連接,根據(jù)連接建立是否成功來(lái)判斷驗(yàn)證成功與否;QueryDatabaseAuthenticationHandler 通過(guò)配置一個(gè) SQL 語(yǔ)句查出密碼,與所給密碼匹配;SearchModeSearchDatabaseAuthenticationHandler 通過(guò)配置存放用戶(hù)驗(yàn)證信息的表、用戶(hù)名字段和密碼字段,構(gòu)造查詢(xún)語(yǔ)句來(lái)驗(yàn)證。
使用哪個(gè) AuthenticationHandler,需要在 deployerConfigContext.xml 中設(shè)置,默認(rèn)情況下,CAS 使用一個(gè)簡(jiǎn)單的 username=password 的 AuthenticationHandler,在文件中可以找到如下一行:<bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePassword
AuthenticationHandler" />,我們可以將其注釋掉,換成我們希望的一個(gè) AuthenticationHandler,比如,使用QueryDatabaseAuthenticationHandler 或 SearchModeSearchDatabaseAuthenticationHandler 可以分別選取清單 5 或清單 6 的配置。
<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> |
<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> |
另外,由于存放在數(shù)據(jù)庫(kù)中的密碼通常是加密過(guò)的,所以 AuthenticationHandler 在匹配時(shí)需要知道使用的加密方法,在 deployerConfigContext.xml 文件中我們可以為具體的 AuthenticationHandler 類(lèi)配置一個(gè) property,指定加密器類(lèi),比如對(duì)于 QueryDatabaseAuthenticationHandler,可以修改如清單7所示:
<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 是對(duì)清單 8 中設(shè)置的實(shí)際加密器類(lèi)的引用:
<bean id="passwordEncoder" class="org.jasig.cas.authentication.handler.MyPasswordEncoder"/> |
這里 MyPasswordEncoder 是根據(jù)實(shí)際情況自己定義的加密器,實(shí)現(xiàn) PasswordEncoder 接口及其 encode() 方法。
3. 部署依賴(lài)包
在以上配置完成以后,需要拷貝幾個(gè)依賴(lài)的包到 cas 應(yīng)用下,包括:
擴(kuò)展 CAS Server 界面
CAS 提供了 2 套默認(rèn)的頁(yè)面,分別為“ default ”和“ simple ”,分別在目錄“ cas/WEB-INF/view/jsp/default ”和“ cas/WEB-INF/view/jsp/simple ”下。其中 default 是一個(gè)稍微復(fù)雜一些的頁(yè)面,使用 CSS,而 simple 則是能讓 CAS 正常工作的最簡(jiǎn)化的頁(yè)面。
在部署 CAS 之前,我們可能需要定制一套新的 CAS Server 頁(yè)面,添加一些個(gè)性化的內(nèi)容。最簡(jiǎn)單的方法就是拷貝一份 default 或 simple 文件到“ cas/WEB-INF/view/jsp ”目錄下,比如命名為 newUI,接下來(lái)是實(shí)現(xiàn)和修改必要的頁(yè)面,有 4 個(gè)頁(yè)面是必須的:
CAS 的頁(yè)面采用 Spring 框架編寫(xiě),對(duì)于不熟悉 Spring 的使用者,在修改之前需要熟悉該框架。
頁(yè)面定制完過(guò)后,還需要做一些配置從而讓 CAS 找到新的頁(yè)面,拷貝“ cas/WEB-INF/classes/default_views.properties ”,重命名為“ cas/WEB-INF/classes/ newUI_views.properties ”,并修改其中所有的值到相應(yīng)新頁(yè)面。最后是更新“ cas/WEB-INF/cas-servlet.xml ”文件中的 viewResolver,將其修改為如清單 9 中的內(nèi)容。
<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> |
![]() ![]() |
![]()
|
部署客戶(hù)端應(yīng)用
單點(diǎn)登錄的目的是為了讓多個(gè)相關(guān)聯(lián)的應(yīng)用使用相同的登錄過(guò)程,本文在講解過(guò)程中構(gòu)造 2個(gè)簡(jiǎn)單的應(yīng)用,分別以 casTest1 和 casTest2 來(lái)作為示例,它們均只有一個(gè)頁(yè)面,顯示歡迎信息和當(dāng)前登錄用戶(hù)名。這 2 個(gè)應(yīng)用使用同一套登錄信息,并且只有登錄過(guò)的用戶(hù)才能訪問(wèn),通過(guò)本文的配置,實(shí)現(xiàn)單點(diǎn)登錄,即只需登錄一次就可以訪問(wèn)這兩個(gè)應(yīng)用。
與 CAS Server 建立信任關(guān)系
假設(shè) CAS Server 單獨(dú)部署在一臺(tái)機(jī)器 A,而客戶(hù)端應(yīng)用部署在機(jī)器 B 上,由于客戶(hù)端應(yīng)用與 CAS Server 的通信采用 SSL,因此,需要在 A 與 B 的 JRE 之間建立信任關(guān)系。
首先與 A 機(jī)器一樣,要生成 B 機(jī)器上的證書(shū),配置 Tomcat 的 SSL 協(xié)議。其次,下載http://blogs.sun.com/andreas/entry/no_more_unable_to_find 的 InstallCert.java,運(yùn)行“ java InstallCert compA:8443 ”命令,并且在接下來(lái)出現(xiàn)的詢(xún)問(wèn)中輸入 1。這樣,就將 A 添加到了 B 的 trust store 中。如果多個(gè)客戶(hù)端應(yīng)用分別部署在不同機(jī)器上,那么每個(gè)機(jī)器都需要與 CAS Server 所在機(jī)器建立信任關(guān)系。
配置 CAS Filter
準(zhǔn)備好應(yīng)用 casTest1 和 casTest2 過(guò)后,分別部署在 B 和 C 機(jī)器上,由于 casTest1 和casTest2,B 和 C 完全等同,我們以 casTest1 在 B 機(jī)器上的配置做介紹,假設(shè) 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 所示:
<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> |
對(duì)于所有訪問(wèn)滿(mǎn)足 casTest1/protected-pattern/ 路徑的資源時(shí),都要求到 CAS Server 登錄,如果需要整個(gè) casTest1 均受保護(hù),可以將 url-pattern 指定為“/*”。
從清單 10 可以看到,我們可以為 CASFilter 指定一些參數(shù),并且有些是必須的,表格 1 和表格 2 中分別是必需和可選的參數(shù):
參數(shù)名 | 作用 |
edu.yale.its.tp.cas.client.filter.loginUrl | 指定 CAS 提供登錄頁(yè)面的 URL |
edu.yale.its.tp.cas.client.filter.validateUrl | 指定 CAS 提供 service ticket 或 proxy ticket 驗(yàn)證服務(wù)的 URL |
edu.yale.its.tp.cas.client.filter.serverName | 指定客戶(hù)端的域名和端口,是指客戶(hù)端應(yīng)用所在機(jī)器而不是 CAS Server 所在機(jī)器,該參數(shù)或 serviceUrl 至少有一個(gè)必須指定 |
edu.yale.its.tp.cas.client.filter.serviceUrl | 該參數(shù)指定過(guò)后將覆蓋 serverName 參數(shù),成為登錄成功過(guò)后重定向的目的地址 |
參數(shù)名 | 作用 |
edu.yale.its.tp.cas.client.filter.proxyCallbackUrl | 用于當(dāng)前應(yīng)用需要作為其他服務(wù)的代理(proxy)時(shí)獲取 Proxy Granting Ticket 的地址 |
edu.yale.its.tp.cas.client.filter.authorizedProxy | 用于允許當(dāng)前應(yīng)用從代理處獲取 proxy tickets,該參數(shù)接受以空格分隔開(kāi)的多個(gè) proxy URLs,但實(shí)際使用只需要一個(gè)成功即可。當(dāng)指定該參數(shù)過(guò)后,需要修改 validateUrl 到 proxyValidate,而不再是 serviceValidate |
edu.yale.its.tp.cas.client.filter.renew | 如果指定為 true,那么受保護(hù)的資源每次被訪問(wèn)時(shí)均要求用戶(hù)重新進(jìn)行驗(yàn)證,而不管之前是否已經(jīng)通過(guò) |
edu.yale.its.tp.cas.client.filter.wrapRequest | 如果指定為 true,那么 CASFilter 將重新包裝 HttpRequest,并且使 getRemoteUser() 方法返回當(dāng)前登錄用戶(hù)的用戶(hù)名 |
edu.yale.its.tp.cas.client.filter.gateway | 指定 gateway 屬性 |
傳遞登錄用戶(hù)名
CAS 在登錄成功過(guò)后,會(huì)給瀏覽器回傳 Cookie,設(shè)置新的到的 Service Ticket。但客戶(hù)端應(yīng)用擁有各自的 Session,我們要怎么在各個(gè)應(yīng)用中獲取當(dāng)前登錄用戶(hù)的用戶(hù)名呢?CAS Client 的 Filter 已經(jīng)做好了處理,在登錄成功后,就可以直接從 Session 的屬性中獲取,如清單 11 所示:
// 以下兩者都可以 session.getAttribute(CASFilter.CAS_FILTER_USER); session.getAttribute("edu.yale.its.tp.cas.client.filter.user"); |
在 JSTL 中獲取用戶(hù)名的方法如清單 12 所示:
<c:out value="${sessionScope[CAS:'edu.yale.its.tp.cas.client.filter.user']}"/> |
另外,CAS 提供了一個(gè) CASFilterRequestWrapper 類(lèi),該類(lèi)繼承自HttpServletRequestWrapper,主要是重寫(xiě)了 getRemoteUser() 方法,只要在前面配置 CASFilter 的時(shí)候?yàn)槠湓O(shè)置“ edu.yale.its.tp.cas.client.filter.wrapRequest ”參數(shù)為 true,就可以通過(guò) getRemoteUser() 方法來(lái)獲取登錄用戶(hù)名,具體方法如清單 13 所示:
CASFilterRequestWrapper reqWrapper=new CASFilterRequestWrapper(request); out.println("The logon user:" + reqWrapper.getRemoteUser()); |
![]() ![]() |
![]()
|
效果
在 casTest1 和 casTest2 中,都有一個(gè)簡(jiǎn)單 Servlet 作為歡迎頁(yè)面 WelcomPage,且該頁(yè)面必須登錄過(guò)后才能訪問(wèn),頁(yè)面代碼如清單 14 所示:
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>"); } } |
在上面所有配置結(jié)束過(guò)后,分別在 A, B, C上啟動(dòng) cas, casTest1 和 casTest2,按照下面步驟來(lái)訪問(wèn) casTest1 和 casTest2:
可以看到圖 中地址欄里的地址多出了一個(gè) ticket 參數(shù),這就是 CAS 分配給當(dāng)前應(yīng)用的 ST(Service Ticket)。
![]() ![]() |
![]()
|
結(jié)束語(yǔ)
本文介紹了 CAS 單點(diǎn)登錄解決方案的原理,并結(jié)合實(shí)例講解了在 Tomcat 中使用 CAS 的配置、部署方法以及效果。CAS 是作為開(kāi)源單點(diǎn)登錄解決方案的一個(gè)不錯(cuò)選擇,更多的使用細(xì)節(jié)可以參考 CAS 官方網(wǎng)站。
聯(lián)系客服