Jasig cas 單點登入系統Server&Java Client配置
cas官網文件訪問地址:https://wiki.jasig.org/display/CASUM/Home。
我下載的是:3.5.2版本。我配置好的cas介面如下:
介面一:
介面二:
下載連結:
已經搞好的Java客戶端及伺服器端(MySQL)配置下載:http://pan.baidu.com/s/11MRPr ,兩個war包,1個jar包:
cas.war,是配置好的cas伺服器端:http://192.168.1.10:8080/casclient 。
casclient.war,是cas客戶端示例,所謂cas客戶端,就是指使用cas進行單點登入的系統: http://192.168.1.10:8080/cas/login。
cas-client-core-3.2.1-SNAPSHOT.jar:是我在版本3.2.1原始碼基礎上進行修改過的,目的是登入成功後,自動跳轉到前一頁。
要修改cas.war包中deployerConfigContext.xml裡面的資料庫連線串及密碼查詢SQL,jasig日誌檔案的位置:tomcat所在磁碟:/data/applogs/jasig/目錄下,可以通過修改WEB-INFO/class/log4j.xml進行配置。
需要的MySQL資料庫privilege建表及資料初始化指令碼:create_privilege.sql
使用這個資料庫,預設的登入帳戶密碼:root/root
最新程式碼Git地址:
Jasig cas伺服器端配置:
Jasig cas的功能是認證(單點登入,SSO,Single Sign On),關於系統的授權,由於各個系統可能都不太一樣,各個系統自行負責為宜。
Jasig cas的配置非常簡單,從官網下載的原始碼包中,有個module目錄,裡面有製作好的war包,可以直接使用,當然也可以自行使用maven打包,命令如下:
mvn clean package -Dmaven.test.skip=true -U
把war包改名為cas.war,放到tomcat的webapps目錄下,就可以使用了,預設使用的是登陸驗證類是測試用的,只要使用者名稱、密碼一直就算登陸成功,為了在生產環境使用,我們改為使用MySql資料庫驗證,步驟如下:
1、簡單起見,我們不使用https認證,做如下更改:
WEB-INF\spring-configuration目錄下ticketGrantingTicketCookieGenerator.xml和warnCookieGenerator.xml,把p:cookieSecure="true"改為p:cookieSecure="false"
WEB-INF目錄下deployerConfigContext.xml中,在如下節點中,增加p:requireSecure="false",
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" p:httpClient-ref="httpClient" p:requireSecure="false" />
2、使用MySql驗證
WEB-INF\deployerConfigContext.xml檔案中, 對<property name="authenticationHandlers">節點,做如下更改: <property name="authenticationHandlers"> <list><bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient" p:requireSecure="false" />
<bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
<property name="dataSource" ref="dataSource" />
<property name="sql" value="select password from p_user where username=?" />
<property name="passwordEncoder" ref="passwordEncoderBean"/>
</bean>
</list>
</property>
根節點,也就是beans節點中,增加如下節點:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8</value>
</property>
<property name="username"><value>root</value></property>
<property name="password"><value>root</value></property>
</bean>
<bean id="passwordEncoderBean" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
<constructor-arg value="MD5" />
</bean>
注意:
同時,需要把cas-server-support-jdbc-3.5.2.jar包放在cas.wab包的WEB-INF目錄下,把mysql-connector-java-5.1.24-bin.jar放入Tomcat的WEB-INF/lib目錄裡。
3、退出時跳轉到service頁
修改src\main\webapp\WEB-INF\cas-servlet.xml裡的logoutController
增加p:followServiceRedirects="true"使支援logout輸入service引數為跳轉路徑。
4、增加免登陸(Remember Me)功能
CAS增加免登陸(Remember Me)功能:
Jasig cas客戶端配置
1、pom.xml配置
<!-- CAS Client -->
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.1.10</version>
</dependency>
如果不使用maven的話,直接下載jar包即可。
2、web.xml配置
<!-- ======================== 單點登入開始 ======================== -->
<!-- 說明:這種客戶端的配置方式是不需要Spring支援的 -->
<!-- 參考資料:http://blog.csdn.net/yaoweijq/article/details/6003187 -->
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
<filter>
<filter-name>CAS Single Sign Out Filter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CAS Single Sign Out Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>http://localhost:8080/cas/login</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Authentication Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>CAS Validation Filter</filter-name>
<filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
<init-param>
<param-name>casServerUrlPrefix</param-name>
<param-value>http://localhost:8080/cas</param-value>
</init-param>
<init-param>
<param-name>serverName</param-name>
<param-value>http://localhost:8080</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CAS Validation Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 該過濾器使得開發者可以通過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 Assertion Thread Local Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- ======================== 單點登入結束 ======================== -->
3、單點退出
單點退出的原理,可以總結為一句話:
A客戶端退出,呼叫cas伺服器的logout,cas server會向所有相關的接入CAS的系統傳送訊息,這些接入CAS的系統監聽到訊息後,觸發session失效。
注意:我們的接入CAS的系統必須是cas伺服器可以訪問到的,如果我們使用:http://localhost:8080/picture的形式訪問站點,則是無法實現單點登出的,因為cas伺服器訪問不到我們的localhost,必須改成:http://ip地址:8080/picture的形式,讓CAS可以訪問到我們的系統。
參考:
更改單點登入介面
WEB-INF\view\jsp\default\ui
登陸介面:casLoginView.jsp
登陸成功:casGenericSuccess.jsp
登出介面:casLogoutView.jsp
cas是支援國際化的,關於頁面中顯示的提示資訊的儲存位置class/*.properties,預設是en,為了讓比較簡單的中文化,我把en做了個備份,然後把zh_CN拷貝成en。
在properties檔案中, 對於非英文的字元,採用16進位制儲存,關於unicode字元和16進位制字元的轉換,提供如下兩個函式:
//把中文字串轉換為十六進位制Unicode編碼字串
public static String stringToUnicode(String s) {
String str = "";
for (int i = 0; i < s.length(); i++) {
int ch = (int) s.charAt(i);
if (ch > 255)
str += "\\u" + Integer.toHexString(ch);
else
str += "\\" + Integer.toHexString(ch);
}
return str;
}
//把十六進位制Unicode編碼字串轉換為中文字串
public static String unicodeToString(String str) {
Pattern pattern = Pattern.compile("(\\\\u(\\p{XDigit}{4}))");
Matcher matcher = pattern.matcher(str);
char ch;
while (matcher.find()) {
ch = (char) Integer.parseInt(matcher.group(2), 16);
str = str.replace(matcher.group(1), ch + "");
}
return str;
}
也可以更改cas-servlet.xml檔案中關於本地化的配置,把:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"/> 修改為:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.FixedLocaleResolver"/>
這個本地化解析器返回一個固定的本地化資訊。預設值為當前JVM的locale。
另外,我在修改一個CAS介面時,執行過程中每次重新整理頁面都報一個警告:WARN:oejh.HttpGenerator:Ignoring extra content,並且點選頁面的submit按鈕,大於在第3或者第5次的時候,頁面就reload了,後來用排除法(逐漸縮小程式碼範圍)找出問題,我的新介面的login_soft.css中,有關於圖片的css:background: url(../../img/bg-white-lock.png) repeat; 但是對應的目錄下沒有相應圖片,導致瞭如上的兩個問題。把圖片都拷貝到相應目錄後,問題解決。為什麼會這樣,原因尚不明,估計與CAS的WebFlow相關程式碼有關,不深入繼續研究了
關於log4j.xml
logger的預設additivity值是true,代表日誌繼續丟擲到root進行處理,此時會被root裡面所有appender處理;
logger設定additivity的值為false,代表日誌被此節點的appender吃掉,不繼續丟擲;
root裡面的<level value="ERROR" />只對空的logger節點才生效,比如:
<logger name="com.github.inspektr.audit.support.Slf4jLoggingAuditTrailManager" additivity="true" />
關於原始碼
伺服器端原始碼(版本:cas-server-3.5.2),實際上我們沒有必要對伺服器端原始碼做更改,需要的配置直接修改xml就可以了: inspektrThrottledSubmissionContext.xml中:應該是spring-beans-3.1.xsd,不能是spring-beans.xsd,否則ref="local"會報錯: cvc-complex-type.3.2.2: Attribute 'local' is not allowed to appear in element 'ref'.cas-server-webapp專案裡面,測試下面的很多xml都是格式不正確(其實可以直接在Eclipse裡面把Validate關掉),報如下錯誤:
The markup in the document following the root element must be well-formed.
avax.servlet.jsp.PageContext cannot be resolved to a type,錯誤,參考: 以上錯誤不影響原始碼編譯。 注意,因為我們計劃用MySQL進行驗證,需要引入JDBC,所以在cas-server-webapp專案的pom.xml中,增加依賴,如下: <dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${project.version}</version>
</dependency>
客戶端原始碼(版本:cas-client-3.2.1),修改其中的cas-client-core:
//修改類:org.jasig.cas.client.util.AbstractCasFilter的constructServiceUrl,修改後內容如下:
//return CommonUtils.constructServiceUrl(request, response, this.service, this.serverName, this.artifactParameterName, this.encodeServiceUrl);
String currentPage = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
+ request.getRequestURI()
+ (CommonUtils.isNotBlank(request.getQueryString()) ? ("?"+request.getQueryString()) : "");
String ticket = request.getParameter("ticket");
//http://192.168.1.200:8080/cas/serviceValidate?ticket=ST-74-bJsAKJivCfkABMWLgViu-cas01.example.org&service=http%3A%2F%2Flocalhost%3A8080%2Fauthority%2Fcontroller%2Fmain.do%3Fticket%3DST-74-bJsAKJivCfkABMWLgViu-cas01.example.org
//去掉引數中所有ticket相關的部分
currentPage = currentPage.replace("%3Fticket%3D"+ticket, "");
currentPage = currentPage.replace("&ticket="+ticket, "");
currentPage = currentPage.replace("?ticket="+ticket, "");
currentPage = currentPage.replace("&service=", "?service");
return currentPage;
//或者修改類:org.jasig.cas.client.util.CommonUtils的constructServiceUrl,修改後被替換後的內容如下:
if (CommonUtils.isBlank(serverName)) {
buffer.append(request.getRequestURL());
}else{
if (!serverName.startsWith("https://") && !serverName.startsWith("http://")) {
buffer.append(request.isSecure() ? "https://" : "http://");
}else{
buffer.append(serverName);
}
buffer.append(request.getRequestURI());
}
目的是,讓客戶端登陸後,自動跳轉到認證前的頁面,這樣,我們就不需要配置cas.service.url(認證成功的返回頁面)引數了,也更合理。
工作原理
如果session有效,則認證通過; 如果session無效,一般是別的系統單點登入,本系統第一次訪問,則ticket認證,並session生成; 如果單點退出, 呼叫cas server的logout,則cas server對所有相關單點發送session失效訊息,單點監聽此訊息並觸發session過期; 可以看到,絕大多數認證都是通過session進行的,所以對於後臺系統來說,不會有任何效能問題。參考文章: