CAS 單點登錄指導文檔
1.概述
單點登錄(Single Sign On),簡稱為 SSO,是目前比較流行的企業業務整合的解決方案之一。SSO的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。
耶魯大學(yale)開發的單點登錄(Single Sign On)系統稱為CAS(Central Authentication Service)被設計成一個獨立的Web應用程序(cas.war)。
CAS在2004年12月成為Jasig項目,所以也叫JA-SIG CAS。
本文中服務器版本基於4.0.0版本,對應的客戶端版本為3.2.1;
官網:http://jasig.github.io/cas/4.0.0/index.html
2. CAS 原理和協議
從結構上看,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 官方網站上的相關文檔。
3.環境
.Tomcat 7.x 本文中安裝目錄為: D:\apache-tomcat-7.0.54
.JDK 7 本文中安裝目錄為: D:\Java\jdk1.7.0_60
. CAS Server版本: cas-server-4.0.0
.CAS Client版本: cas-client-3.2.1
4.配置證書
證書是單點登錄認證系統中很重要的一把鑰匙,客戶端於服務器的交互安全靠的就是證書;本教程由於是演示所以就自己用JDK自帶的keytool工具生成證書;如果以后真正在產品環境中使用肯定要去證書提供商去購買,證書認證一般都是由VeriSign認證,中文官方網站:http://www.verisign.com/cn/。
也可以申請免費的StartSSL CA證書: StartSSL(公司名:StartCom)也是一家CA機構,它的根證書很久之前就被一些具有開源背景的瀏覽器支持(Firefox瀏覽器、谷歌Chrome瀏覽器、蘋果Safari瀏覽器等)。 申請地址:http://www.startssl.com 申請方法參考:http://www.linuxidc.com/Linux/2011-11/47478.htm
1> 創建證書
首先創建文件夾:D:/cas/keys用於存放相關的證書文件。
然后打開windows 命令控制台:(開始 --> cmd ->回車)
切換到JDK安裝目錄中Key: D:\Java\jdk1.7.0_60\jre\bin
輸入以下命令:
keytool -genkey -alias mycas -keyalg RSA -keysize 2048 -keystore d:/cas/keys/mycas.keystore
接下來會有提示 如圖2:
圖2. 創建證書:
此步完成后可在D:/cas/keys文件夾下找到mycas.keystore文件
2>導出證書
輸入命令:
keytool -export -file d:/cas/keys/mycas.crt -alias mycas -keystore d:/cas/keys/mycas.keystore
此步完成后可在D:/cas/keys文件夾下找到mycas.crt文件
3>客戶端JVM導入證書
在客戶端的JDK安裝目錄下輸入命令:
keytool -import -keystore d:\Java\jdk1.7.0_60\jre\lib\security\cacerts -file d:/cas/keys/mycas.crt -alias mycas
其中:d:\Java\jdk1.7.0_60\jre\lib\security\cacerts為客戶端JVM的密鑰庫位置
如果提示:
keytool error: java.io.IOException: Keystore was tampered with, or password was incorrect
那么輸入密碼:changeit
這是因為JDK安裝后會默認創建一個密鑰庫,密碼為:changeit
也可以刪除d:\Java\jdk1.7.0_60\jre\lib\security\cacerts 在輸入上述命令,如圖3
圖3. 導入證書:
4>將證書應用到tomcat
打開tomcat目錄的conf/server.xml文件,8443端處,並設置keystoreFile、keystorePass修改結果如下:
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="D:/cas/keys/mycas.keystore"
keystorePass="生成KEY的密碼"
/>
參數說明:keystoreFile:在第一步創建的key存放位置 keystorePass:創建證書時的密碼。
打開https://localhost:8443/,可以測試,如圖4:
圖4,測試證書
點擊“繼續瀏覽此網站(不推薦),現在進入Tomcat目錄了吧,如果是那么你又向成功邁進了一步。
5.CAS Server端配置
解壓縮cas-server-4.0.0 jar包 ,將modules文件夾下的cas-server-webapp-4.0.0.war拷貝到D:\apache-tomcat-7.0.54\webapps目錄下,改名為cas.war(只是為了方便URL輸入),並啟動Tomcat, 會自動解壓得到cas工程文件夾。
好了,現在打開瀏覽器,輸入https://localhost:8443/cas/login ,顯示如圖5:
圖5 .CAS默認登錄頁面
CAS Server 4.0版本默認采用的username/password 在cas\WEB-INF下的deployerConfigContext.xml中是這樣的:
<bean id="primaryAuthenticationHandler"
class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
<property name="users">
<map>
<entry key="casuser" value="Mellon"/>
</map>
</property>
</bean>
輸入username: casuser password:Mellon 登錄成功,如圖6:
圖6.CAS 默認登錄成功
你成功了嗎?如果沒有成功請再檢查以上步驟!
6.CAS Server 用戶認證定制
上面的初體驗僅僅是簡單的身份驗證,實際應用中肯定是要讀取數據庫的數據,下面我們來進一步配置CAS服務器怎么讀取數據庫的信息進行身份驗證,即cas-server-support-jdbc。
首先,打開D:\apache-tomcat-7.0.54\webapps\cas\WEB-INF\deployerConfigContext.xml,
注釋掉默認的用戶驗證,添加JDBC認證,代碼如下:
<!--<bean id="primaryAuthenticationHandler"
class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
<property name="users">
<map>
<entry key="casuser" value="huiquan"/>
</map>
</property>
</bean> -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="com.mysql.jdbc.Driver"p:jdbcUrl="jdbc:mysql://192.168.0.10:3306/college?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"
p:user="root"
p:password="root" />
<!-- 密碼加密方式-->
<bean id="passwordEncoder"
class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"
c:encodingAlgorithm="SHA1"
p:characterEncoding="UTF-8" />
<bean id="dbAuthHandler"
class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"
p:dataSource-ref="dataSource"
p:sql="select password from college.pub_party where partyno=? and partytypecode='0000006' and data_status='1'" />
<!-- p:passwordEncoder-ref="passwordEncoder" --><!-- 暫時不使用密碼加密 -->
根據上述配置,我們需要在D:\apache-tomcat-7.0.54\webapps\cas\WEB-INF\lib中加入c3p0-0.9.1.2.jar包、mysql-connector-java-5.1.21.jar包和cas-server-support-jdbc-4.0.0.jar包。
再修改D:\apache-tomcat-7.0.54\webapps\cas\WEB-INF\deployerConfigContext.xml中authentication manager的配置:
<bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
<constructor-arg>
<map>
<entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
<!--<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> -->
<entry key-ref="dbAuthHandler" value-ref="primaryPrincipalResolver"/>
</map>
</constructor-arg>
<property name="authenticationPolicy">
<bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy" />
</property>
</bean>
再次運行Tomcat,訪問https://localhost:8443/cas/login就能進行數據庫驗證了。
7.CAS Client 配置
本文中客戶端工程部署在同一個Tomcat中,所以CAS Server和Client的域名都為localhost。
在Client工程WEB-INF/lib下添加cas-client-core-3.2.1.jar包。
修改web.xml如下:
<!-- ======================== 單點登錄/登出 ======================== -->
<!-- 該過濾器用於實現單點登出功能,可選配置。 -->
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<!-- 該過濾器負責用戶的認證工作,必須啟用它 -->
<filter>
<filter-name>CAS Authentication Filter</filter-name>
<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
<init-param>
<param-name>casServerLoginUrl</param-name>
<param-value>https://localhost:8443/cas/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<!-- 該過濾器負責對Ticket的校驗工作,必須啟用它 -->
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas10TicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>https://localhost:8443/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
<init-param>
<param-name>redirectAfterValidation</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 該過濾器負責實現HttpServletRequest請求的包裹,
比如允許開發者通過HttpServletRequest的getRemoteUser()方法獲得SSO登錄用戶的登錄名,可選配置。 -->
<filter>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
</filter>
<!-- 該過濾器使得開發者可以通過org.jasig.cas.client.util.AssertionHolder來獲取用戶的登錄名。
比如AssertionHolder.getAssertion().getPrincipal().getName()。-->
<filter>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CAS Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<!-- ======================== 單點登錄/登出結束 ======================== -->
根據現在運行Client工程,首次訪問任一頁面就會跳轉到https://localhost:8443/cas/login進行認證。同時,把你的退出鏈接設置為:https://sso.wsria.com/cas/logout 即可實現單點推出。
8、美化CAS服務器界面
CAS服務端(cas-server)的界面只能在測試的時候用一下,真正系統上線肯定需要定制開發自己的頁面,就像網易和CSDN的統一認證平台一樣,所有子系統的認證都通過此平台來轉接,大家可以根據他們的頁面自己定制出適合所屬應用或者公司的界面;簡單介紹一下吧,復制 cas\WEB-INF\view\jsp\default\ui的一些JSP文件,每一個文件的用途文件名已經區分了,自己修改了替換一下就可以了。 例如:
登錄界面:casLoginView.jsp
登錄成功:casGenericSuccess.jsp
登出界面:casLogoutView.jsp
9、更新Client端頁面跳轉配置
完成前面的步驟,並不能得到我們理想的效果。例如,我們的目標是:
Client端index頁面 -> CAS Server端login頁面 -> 認證成功 -> Client端index頁面,並顯示歡迎信息。
但由於Client端有自己的登錄驗證攔截機制,效果卻是這樣的:
Client端index頁面 -> CAS Server端login頁面->認證成功 -> Client端login頁面。
所以,我需要修改Client端的認證攔截/跳轉頁面。
前面的步驟以后,我們在Client端的HttpServletRequest中已經可以通過getRemoteUser()方法得到用戶名了。
String username = request.getRemoteUser();
再據此修正控制器login頁面跳轉或攔截器就行了。
結束語
本文只是簡單的介紹了CAS的搭建和JDBC認證定制。CAS還提供了多種認證方式:如LADP、legacy、trusted、x.509等等,更多深入的擴展則需要重寫AuthenticationHandler的實現等
在此列出一些比較好的相關博文以供參考:
•http://blog.csdn.net/frinder/article/details/7969925
•http://sgq0085.iteye.com/blog/2003190
•http://sgq0085.iteye.com/blog/2099196
•http://www.iteye.com/blogs/tag/cas