1. 程式人生 > >Jasig cas 單點登入系統Server&Java Client配置

Jasig cas 單點登入系統Server&Java Client配置

Jasig cas(Central Authentication Service)官方站點:http://www.jasig.org/cas,訪問這個站點需要翻牆。

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&amp;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進行的,所以對於後臺系統來說,不會有任何效能問題。

參考文章: