【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 方法中调用了AuthenticationHandler 的authenticate 的方法 ,authenticate 方法就会调用我们上面自定义的 BlogUsersAuthenticationHandler  方法
  7. 然后根据我们写的方法返回的相关信息调用 resolvePrincipal 方法,把credential 类型的信息转变为Principal类型的信息,即组装我们需要返回给客户端的一些信息。这个主要是通过 PrincipalResolver 类进行转变得,第四篇中有重点说到,这边就不细讲了。
  8. 最终成功登陆到客户端

上面的流程只是认证的主要流程,不包含ST的生成、验证等过程。

4.总结

通过数据库认证基本上写完了,不过上面只是简单的演示了下,大家需要根据自己的情况进行修改。

如果上面的内容有错误,欢迎大家指出。也欢迎大家多多留言。大家共同进步。

顺便祝大家情人节快乐新年快乐

打完收工...

时间: 2024-08-07 10:18:36

【SSO单点系列】(7):CAS4.0 SERVER通过数据库方式认证用户的相关文章

【SSO单点系列】(1):CAS4.0 之环境的搭建

[SSO单点系列](1):CAS4.0 环境的搭建 一.概述 今天开始写CAS相关的第一篇文章,这篇文章主要是关于CAS环境的搭配,提供给刚刚接触CAS的一个入门指南,并演示一个CAS的最简单的实例 二.环境要求 博主的环境如下: win8.1 64 bit JDK1.7  下载地址点我 Tomcat-8.0.15  下载地址点我 cas-server-4.0.0 .cas-client-3.3.3  下载地址点我  (官网速度比较慢,提供百度网盘) tomcat服务器需要部署三个,我分别命名为

【SSO单点系列】(2):CAS登录页的个性化定制

上一篇 [SSO单点系列](1):CAS环境的搭建介绍了CAS最简单环境的搭建,以及一个例子用来讲解CAS的一个最基础的用法. 今天主要是介绍如何对CAS登录页进行个性化定制.    一.开始 下图是CAS默认的登录界面,可以看到这界面是肯定不能直接用在生产环境上的,因为上面的有许多英文,简单来说,这是一个对客户非常不友好的界面.那么怎么修改它呢?我们接着往下看! ps:这个页面在工程中的地址为 cas\WEB-INF\view\jsp\default\ui\casLoginView.jsp,大

spring cloud云架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)

上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: Java代码   /** * 根据token获取用户信息 * @param accessToken * @return * @throws Exception */ @RequestMapping(value = "/user/token/{accesstoken}", method = RequestMethod.GET) pu

Spring Cloud云架构 SSO单点登录之OAuth2.0 根据token获取用户信息(4)

上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: /** * 根据token获取用户信息 * @param accessToken * @return * @throws Exception */ @RequestMapping(value = "/user/token/{accesstoken}", method = RequestMethod.GET) public Resp

SSO单点登录之OAuth2.0 根据token获取用户信息(4)

上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: /** * 根据token获取用户信息 * @param accessToken * @return * @throws Exception */ @RequestMapping(value = "/user/token/{accesstoken}", method = RequestMethod.GET) public Resp

整合spring cloud云架构 - SSO单点登录之OAuth2.0 根据token获取用户信息

上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: /** * 根据token获取用户信息 * @param accessToken * @return * @throws Exception */ @RequestMapping(value = "/user/token/{accesstoken}", method = RequestMethod.GET) public Resp

(十三)Java springcloud B2B2C o2o多用户商城 springcloud架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)

上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: /** * 根据token获取用户信息 * @param accessToken * @return * @throws Exception */ @RequestMapping(value = "/user/token/{accesstoken}", method = RequestMethod.GET) public Resp

(十三) 整合spring cloud云架构 - SSO单点登录之OAuth2.0 根据token获取用户信息(4)

上一篇我根据框架中OAuth2.0的使用总结,画了SSO单点登录之OAuth2.0 登出流程,今天我们看一下根据用户token获取yoghurt信息的流程: /** * 根据token获取用户信息 * @param accessToken * @return * @throws Exception */ @RequestMapping(value = "/user/token/{accesstoken}", method = RequestMethod.GET) public Resp

【SSO单点系列】(7):CAS4.0 二级域名

CAS4.0 二级域名 一.描述 当cas成功登录后如果访问同一域名下的资源是 被当作同一应用下资源不需要再次请求登录,但是如果二级域名不同会 被当作不同应用在访问 需要请求CAS 在请求时会把TGC传给CAS  CAS验证TGC有效返回资源页面 本身是没有问题的  但是在返回时地址 会被重写 就不再是新生成的二级域名了 所以项目中无法区分二级域名了 二.思路 在登录成功后就自己写入session不用原来的域名 三.实现 AuthenticationFilter.java 方法 doFilter