工作需要,對CAS進行了研究,網(wǎng)絡上看了些文章,但都不完善,自己就寫了一篇。
我在CAS部署過程中遇到了幾個問題,主要是中文亂碼的問題,畢竟是外國人寫的東西。對中文的支持不好。
相關的jar包可以在www.apache.org www.jasig.org www.springsource.org上找到
在CAS架設中遇到的問題和我的解決方案
1.在CAS服務端用戶登入如果是中文請求會亂碼的問題
解決方案:CAS利用到spring,spring框架中提供了編碼過濾功能,利用它可以解決請求亂碼問題
2.CAS客戶端和服務端中文傳輸亂碼問題,雖然請求亂碼問題解決了,但是這個問題還是會存在,可能是數(shù)據(jù)傳輸過程中引起的
解決方案:BASE64服務端加密,客戶端解密(O(∩_∩)O~偶突發(fā)奇想來的)
3.中心同一個服務器,機房和辦公室訪問地址不同的問題(有的網(wǎng)站一個服務器有多個域名)
解決方案:重寫CAS客戶端CASFilter類,請注意,我們中心的CAS客戶端和CAS服務端在同一服務器上的,我是針對這環(huán)境下寫的
CAS架設環(huán)境
Windows
Tomcat 6.x
JDK 1.6
CAS server 3.34
cas-client-2.0.11(是耶魯大學的那個版本,不是最新版,最新不一定最好,下載時請注意)
下載最新的cas-server-3.3.4.zip 服務器端,解壓后找到cas-server-webapp.war,將這個war進行解壓到tomcat的webapps目錄下.
將剛才解壓出的目錄cas-server-webapp進行重命名為cas
啟動tomcat 輸入http://localhost:8080/cas/login 查看是否成功部署
下載地址: http://www.jasig.org/
推薦環(huán)境:JDK1.6 tomcat 6.0
用戶的認證信息通常保存在數(shù)據(jù)庫中,我們這里使用的是jtds數(shù)據(jù)庫連接。
將前面下載的cas-server-3.3.4.zip 包解開后,在 modules 目錄下可以找到包cas-server-support-jdbc-3.3.4.jar,再下載個jtds的jar包,將兩個包拷貝到cas\WEB-INF\lib目錄下
DataStore 依賴于 spring.jar, commons-collections.jar, commons-dbcp.jar, commons-pool.jar
在apache和spring官方網(wǎng)找到這四個包把它們拷貝到cas\WEB-INF\lib下
在 cas-server-support-jdbc-3.3.4.jar包中,提供了 3 個基于 JDBC 的 AuthenticationHandler,分別為 BindModeSearchDatabaseAuthenticationHandler, QueryDatabaseAuthenticationHandler, SearchModeSearchDatabaseAuthenticationHandler
我們這里使用的是QueryDatabaseAuthenticationHandler,它是通過配置一個 SQL 語句查出密碼,與所給密碼匹配
根據(jù)密碼加密方式的不同,我們需要實現(xiàn)PasswordEncoder接口,來對輸入的密碼進行加密才能與數(shù)據(jù)庫中的密碼比較
建立一個項目,導入cas-server-core-3.3.4.jar包
在項目中寫個類實現(xiàn)org.jasig.cas.authentication.handler.PasswordEncoder接口中的public String encode(String arg0)方法,這是用來輸入的密碼加密的。
項目導出成jar包,拷貝到cas\WEB-INF\lib下
根據(jù)不同的密碼加密方式實現(xiàn),我這里是MD5加密,下文僅為參考,請根據(jù)需求變化
新建個caspasskey項目
源碼為
package org;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.jasig.cas.authentication.handler.PasswordEncoder;
public class MD5 implements PasswordEncoder {
@Override
public String encode(String arg0) {
MessageDigest digest = null;
if (digest == null) {
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
nsae.printStackTrace();
}
}
// Now, compute hash.
digest.update(arg0.getBytes());
byte[] bytes=digest.digest();
StringBuffer buf = new StringBuffer(bytes.length * 2);
for (int i= 0; i < bytes.length; i++) {
if (((int) bytes[i] & 0xff) < 0x10) {
buf.append("0");
}
buf.append(Long.toString((int) bytes[i] & 0xff, 16));
}
return buf.toString().toUpperCase();
}
}
導出為caspasskey.jar,拷貝到cas/WEB-INF/lib目錄下
打開文件cas/WEB-INF/deployerConfigContext.xml,
找到
<bean
class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
將它替換為
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="casDataSource" />
<property name="sql"
value="select 密碼 from 用戶表 where lower([用戶名]) = UPPER(?)" />
<property name="passwordEncoder" ref="myPasswordEncoder"/></bean>
<bean id="myPasswordEncoder" class="org.MD5"/> ---org.MD5這是我上文自定義的加密類
找到
<bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl" />在它的下方添加以下數(shù)據(jù)源代碼
<bean id="casDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>net.sourceforge.jtds.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:jtds:sqlserver://數(shù)據(jù)庫地址:1433/數(shù)據(jù)庫名</value>
</property> <property name="username">
<value>數(shù)據(jù)庫訪問用戶名</value> </property>
<property name="password">
<value>數(shù)據(jù)庫訪問密碼</value>
</property>
</bean>
到此,數(shù)據(jù)庫與cas服務端的連接已完成,登入http://localhost:8080/cas/login,用英文的用戶名測試登入是否成功!
中文的用戶名登入,默認情況下是亂碼的,因為cas沒有對請求進行utf-8編碼
cas用到了spring的框架,我們可以直接利用spring的編碼器進行utf-8編碼
在web.xml中添加
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
需要注意的是,此配置需要在 <filter-mapping>
<filter-name>CAS Client Info Logging Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>的上方
再測試,發(fā)現(xiàn)中文可以登入了。
因為cas默認對中文是不支持的,在cas服務端與客戶端之間,如果有中文存在,會有中文亂碼的問題,雖然上文對請求進行了編碼,但是客戶端中文亂碼的問題始終存在。為了避免中文亂碼的存在,我的臨時解決方案是在服務器端對中文進行base64加密,在客戶端進行base64解密
找到org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver類的源代碼,這個類在cas-server-core-3.3.4.jar包中
找到 protected String extractPrincipalId(final Credentials credentials)方法,將
return usernamePasswordCredentials.getUsername();
改為
return (new sun.misc.BASE64Encoder()).encode(usernamePasswordCredentials.getUsername().getBytes());
這樣就可以在服務器端對所有用戶名進行base64加密了
服務器端配置到此結束
如果要實現(xiàn)CAS的單點登入,這是必須的
1. 生產(chǎn)密鑰
點擊’開始’à’運行’,輸入cmd
進入控制臺
Ø CD X:\jdk根目錄\Java\jdk1.6.0_14\bin
Ø CD X:
Ø 下文中導入過程密碼統(tǒng)一使用changeit
keytool -genkey -alias cas-server -keyalg RSA -keypass changeit -storepass changeit -keystore casserver.keystore
輸入密碼后,在第一個提示輸入姓名的時候,輸入你的服務端地址或ip地址,如localhost
國家輸入CN
keytool -export -alias cas-server -storepass changeit -file casserver.cer -keystore casserver.keystore
keytool -import -trustcacerts -alias server -file casserver.cer –keystore jre根目錄/lib/security/cacerts -storepass changeit
如果操作成功,會在jdk bin目錄下看到casserver.cer casserver.keystore 這兩個文件
打開tomcat目錄下conf/ server.xml 文件,找到
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
將注釋去掉,修改為
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" scheme="https" secure="false"
clientAuth="false" sslProtocol="TLS"
keystoreFile=" x:\casserver.keystore 所在的目錄\casserver.keystore" keystorePass="changeit"
/>
默認的密鑰有效期是3個月,可以根據(jù)自己需求修改
訪問https://localhost:8443/cas/login 測試是否部署成功
CAS客戶端配置非常的簡單,但這里需要進行些修改
以下是官方文檔中客戶端web.xml中配置
<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://compA:8443/cas/login</param-value><!—compA是cas服務器的地址à
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>https://compA:8443/cas/serviceValidate</param-value>
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>compA:8080</param-value><!—注意,這里compA是cas客戶端的地址à
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<!—對訪問/servlet/HelloWorldExample 進行攔截,轉向cas服務器端驗證à
<url-pattern>/servlet/HelloWorldExample</url-pattern>
</filter-mapping>
因為我們中心的網(wǎng)絡環(huán)境原因,機房和辦公室訪問同一臺服務器的地址是不同的,所以需要對CASFilter類的源代碼進行更改。注意,此設置修改僅對于CAS客戶端和cas服務端在同一服務器時
下載cas-client-2.0.11.zip解壓導入源碼,修改edu.yale.its.tp.cas.client.filter.CASFilter類
注意:不要太高版本,2.0x是耶魯?shù)陌姹荆甙姹臼?/span>jasig的,實現(xiàn)的步驟全都變了.
1. 聲明兩個屬性
private String casclientport,casserverport;
2. 在init方法中添加
casclientport = config.getInitParameter(
"edu.yale.its.tp.cas.client.filter.port");
casserverport = config.getInitParameter(
"edu.yale.its.tp.cas.server.filter.port");
這是我自定義的,用來在xml中指定自定義的端口號
3. 在doFilter方法中添加
casLogin="https://"+request.getServerName()+":"+casserverport+"/cas/login";//cas服務器的登入地址 request.getServerName()是可以根據(jù)客戶訪問的服務器端地址不同而變化的
casServerName=request.getServerName()+":"+casclientport;//注意,這是cas客戶端的地址
casValidate="https://"+request.getServerName()+":"+casserverport+"/cas/serviceValidate";//cas服務器的驗證地址
修改客戶端web.xml
<!-- CAS -->
<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>/cas/login</param-value><!-- 默認無需修改 -->
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.validateUrl</param-name>
<param-value>/cas/serviceValidate</param-value><!-- 默認無需修改 -->
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.port</param-name>
<param-value>8080</param-value><!-- 對應客戶端端口號 -->
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.server.filter.port</param-name>
<param-value>8443</param-value><!-- 對應服務器端口號 因為是ssl安全連接,所以是8443-->
</init-param>
<init-param>
<param-name>edu.yale.its.tp.cas.client.filter.serverName</param-name>
<param-value>localhost:8080</param-value><!-- 這是服務器端驗證后訪問客戶端的地址-->
</init-param>
</filter>
<!-- 對訪問/xxxxx的都進行攔截,并轉向cas服務器,進行登入驗證 -->
<filter-mapping>
<filter-name>CAS Filter</filter-name>
<url-pattern>/xxxxx</url-pattern>
</filter-mapping>
<!-- CAS end -->
因為我們上文對中文在服務器端進行了base64加密,在客戶端,就需要進行解密
還是對CASFilter類進行修改
在讀doFilter方法中找到以下語句
if (session != null) // probably unncessary
session.setAttribute(CAS_FILTER_USER, user);
將它修改為
if (session != null) // probably unncessary
{
session.setAttribute(CAS_FILTER_USER, new String((new BASE64Decoder()).decodeBuffer(user)));
}
這就是對base64加密的數(shù)據(jù)進行解密后再存放進客戶端的session.
到此客戶端存在的問題就解決了
測試在一個客戶端已http://localhost:8080/客戶端名/xxxxx
1. 我的理解是CAS主要解決用戶登入的問題,權限上還是由各應用系統(tǒng)來分配的,用戶在訪問受限的頁面時,通過過濾器判斷CAS是否已登入過了,并且在客戶端附加過了相關權限。
l 如果用戶未登入過,則登入CAS服務端進行用戶登入,登入后返回客戶端,再根據(jù)用戶名附加權限
l 如果用戶cas服務端已登入過了,但是他是第一次登入客戶端,則進入CAS服務端,獲取登入的用戶名,再返回客戶端進行根據(jù)用戶名附加權限
l 如果用戶已登入了CAS并且附加了權限,則不在進入CAS服務端驗證
全文到此結束,謝謝!
如對本文有疑問,請?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會為你解答??! 點擊進入論壇