【SSO單點系列】(7):CAS4.0 SERVER通過數據庫方式認證用戶


  在前幾篇中有簡單介紹服務端的認證方式,默認的是直接在 deployerConfigContext.xml 文件中 一個叫做 primaryAuthenticationHandler 的bean中配置。不過這只支持一個賬號,而且是固定的,這有非常大的局限性,在現實系統中是肯定不能用這樣的方式的。

現在的應用系統一般都是通過讀取數據庫的方式驗證用戶名、密碼是否正確,進而進行認證的。因此在這一篇文章中將會介紹,怎么把服務端的默認認證方式改造成數據庫驗證的方式,以此滿足系統的基本需求。

 

1.增加數據源配置

數據源的配置和平常我們配置的差不多,CAS可以使用Spring方式進行配置,為了和原來的配置文件分開,我新建了一個叫做 applicationContext-datasource.xml 的配置用來存放數據源的相關配置,(放在cas-server-webapp\src\main\webapp\WEB-INF\spring-configuration下)具體如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
    <description>datasource</description>

   <bean id="casDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">

        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <property name="driverClassName" value="${driverClassName}" />
   
        <property name="maxActive" value="${maxActive}" />  
        <property name="initialSize" value="${initialSize}" />  
        <property name="maxWait" value="${maxWait}" />  
        <property name="minIdle" value="${minIdle}" />  
  
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />  
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />  
  
        <property name="validationQuery" value="${validationQuery}" />  
        <property name="testWhileIdle" value="${testWhileIdle}" />  
        <property name="testOnBorrow" value="${testOnBorrow}" />  
        <property name="testOnReturn" value="${testOnReturn}" />  
        <property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}" />  
        <property name="removeAbandoned" value="${removeAbandoned}" /> <!-- 打開removeAbandoned功能 -->  
        <property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" /> <!-- 1800秒,也就是30分鍾 -->  
        <property name="logAbandoned" value="${logAbandoned}" /> <!-- 關閉abanded連接時輸出錯誤日志 -->  
    </bean> 
    
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="casDataSource" /> 
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"  
        p:dataSource-ref="casDataSource" />
    
    <!-- 通過AOP配置提供事務增強,讓AccountService下所有Bean的所有方法擁有事務 -->
    <aop:config>
        <aop:pointcut id="serviceMethod" expression=" execution(* com.blog.cas.account.service.impl..*(..))" />
        <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" />
    </aop:config>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="get*" propagation="REQUIRED" read-only="true"  />
            <tx:method name="update*" propagation="REQUIRED" />
        </tx:attributes>
    </tx:advice>
    
    
    <bean id="accountService" class="com.blog.cas.account.service.impl.AccountServiceImpl" p:accountDao-ref="accountDao" />
    <bean id="accountDao" class="com.blog.cas.account.dao.impl.AccountDaoImpl" p:jdbcTemplate-ref="jdbcTemplate" />
    
</beans>

注:在這邊有定義一個Service、一個Dao ,是用來與數據庫進行交互的,里面大家寫自己需要的方法就行了。這邊使用的Spring提供的JdbcTemplate 進行查詢。這兩個類就不貼出來了,大家自由實現

 

然后數據源的相關信息,我直接放在文件 cas.properties(cas-server-webapp\src\main\webapp\WEB-INF\cas.properties), 在最后增加下面的內容:

##
# Jdbc
url=jdbc:oracle:thin:@192.168.1.101:1521:odsorcl
username=blog
password=blog

driverClassName=oracle.jdbc.driver.OracleDriver
validationQuery=SELECT 1 from dual 

filters=stat  
maxActive=20  
initialSize=1  
maxWait=60000  
minIdle=10  
timeBetweenEvictionRunsMillis=60000  
minEvictableIdleTimeMillis=300000  
testWhileIdle=true  
testOnBorrow=false  
testOnReturn=false  
maxOpenPreparedStatements=20  
removeAbandoned=true  
removeAbandonedTimeout=1800  
logAbandoned=true

 

 用的是oracle數據庫。

 

2.自定義認證Handler類

cas 默認使用的認證類為 org.jasig.cas.authentication.AcceptUsersAuthenticationHandler。我們看看它的源碼,發現認證是在一個叫做 authenticateUsernamePasswordInternal 的方法中進行的,其實看方法名大家都能猜出來這個方法是做什么的。然后這個類的父類是AbstractUsernamePasswordAuthenticationHandler ,那么我們也繼承這個類,實現authenticateUsernamePasswordInternal方法其實就可以了。

這邊還要注意一下,authenticateUsernamePasswordInternal  方法中的參數是一個 UsernamePasswordCredential 類型的參數,里面其實包含了我們在頁面上輸入的用戶相關信息,即用戶名和密碼。好了知道方法了,那么就動手吧。

public class BlogUsersAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {

    private AccountServiceImpl accountService;
    
    @Override
    protected HandlerResult authenticateUsernamePasswordInternal(
            UsernamePasswordCredential credential)
            throws GeneralSecurityException, PreventedException {
        
        String username = credential.getUsername();
        String password = credential.getPassword();
        
        boolean flag = accountService.checkAccount(username, password);
        if (!flag) {
           throw new FailedLoginException();
        }
        return createHandlerResult(credential, new SimplePrincipal(username), null);
    }

    //省略get/set 方法

    
}

 

這邊只是一個簡單的驗證邏輯,實際上可能會復雜點,比如判斷用戶的狀態、是否禁用等等。

然后修改相關的配置,打開文件 cas-server-webapp\src\main\webapp\WEB-INF\deployerConfigContext.xml 找到 id 為 primaryPrincipalResolver 的bean ,把這個修改成我們新增的類

 <!-- <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
        <property name="users">
            <map>
                <entry key="admin" value="admin"/>
            </map>
        </property>
    </bean> -->

 
    <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.authentication.BlogUsersAuthenticationHandler">
          <property name="accountService" ref="accountService" />
    </bean>

 

好了,現在已經修改成通過數據庫認證的方式了,大家可以試試看。

3.認證流程

看完了上面的,大家可能會覺得有點不理解。為什么只加了一個類,覆蓋了其中的方法就完成了認證呢,在這節中介紹下大概的一個認證流程,以及到最終的信息返回給客戶端的這樣的一個流程。

其中一些內容在第四篇中有介紹了,最好先了解下第四篇 登錄后用戶信息的返回 相關的內容, 傳送門

  1. 用戶登錄頁輸入相關信息,點擊submit登陸
  2. 執行AuthenticationViaFormAction 類中的  submit 方法中
  3. submit方法中 調用 CentralAuthenticationService 類的grantServiceTicket 方法,其中有傳入 Credential 類型的參數
  4. 接着在 grantServiceTicket方法中 調用了 AuthenticationManager類(實際類為PolicyBasedAuthenticationManager)的authenticate 方法
  5. authenticate方法中又調用了authenticateInternal 方法
  6. 最終在 authenticateInternal 方法中調用了AuthenticationHandlerauthenticate 的方法 ,authenticate 方法就會調用我們上面自定義的 BlogUsersAuthenticationHandler  方法
  7. 然后根據我們寫的方法返回的相關信息調用 resolvePrincipal 方法,把credential 類型的信息轉變為Principal類型的信息,即組裝我們需要返回給客戶端的一些信息。這個主要是通過 PrincipalResolver 類進行轉變得,第四篇中有重點說到,這邊就不細講了。
  8. 最終成功登陸到客戶端

 

上面的流程只是認證的主要流程,不包含ST的生成、驗證等過程。

 

4.總結

通過數據庫認證基本上寫完了,不過上面只是簡單的演示了下,大家需要根據自己的情況進行修改。

如果上面的內容有錯誤,歡迎大家指出。也歡迎大家多多留言。大家共同進步。

順便祝大家情人節快樂新年快樂

打完收工...

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM