cas(三)之数据源

  在默认的cas服务端访问,之前说过使用一样的用户名和密码即可登录成功,但是生产上的用户名和密码都是已经被保存好的,有些在LDAP里面,而有些在数据库里面,下面就介绍cas服务端连接数据库来获取动态的用户名和密码,对应的配置在deployerConfigContext.xml文件里面,下面介绍cas集成数据源实现通过查询数据库来确定用户名密码是否正确。

  • 集成cas-server支持jdbc模块和mysql驱动
    <properties>
            <mysql.version>5.1.21</mysql.version>
            <druid.version>1.0.23</druid.version>
    </properties>
    
    <dependencies>
          <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
    </dependencies>    

     

  我采用druid的数据库连接池。

  • 修改deployerConfigContext.xml配置文件,更改用户名密码认证查询方式

  先新增一个数据源

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="jdbc:mysql://ip:port/database?useUnicode=true&amp;characterEncoding=utf-8" />
        <property name="username" value="username" />
        <property name="password" value="password" />

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="5" />
        <property name="minIdle" value="5" />
        <property name="maxActive" value="50" />

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />

        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="300000" />

        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />

        <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
        <property name="poolPreparedStatements" value="false" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />

        <!-- 配置监控统计拦截的filters -->
        <property name="filters" value="stat" />
    </bean>

 

  然后在文件找到authenticationHandlers,将org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler注释掉,然后新增下面内容

<bean class="org.jasig.cas.adaptors.jdbc.SearchModeSearchDatabaseAuthenticationHandler">
   <property name="dataSource" ref="dataSource" />
   <!-- 密码加密器-->
   <property name="passwordEncoder" ref="passwordEncoder"/>
   <!-- 表名字段-->
    <property name="tableUsers" value="sys_user"/>
   <!-- 用户名字段-->
    <property name="fieldUser" value="account"/>
   <!-- 密码字段-->
    <property name="fieldPassword" value="`PASSWORD`" />
</bean>

  至此,登录用户名和密码查询数据库集成成功。下面介绍一下数据库查询用户名和密码几个类

  • 默认的SimpleTestUsernamePasswordAuthenticationHandler

  这个逻辑就是cas给的默认逻辑,也就是上面所说的用户名和密码一致便可成功登陆系统,通过下面的源代码很清楚的知道

public final class SimpleTestUsernamePasswordAuthenticationHandler extends
    AbstractUsernamePasswordAuthenticationHandler {

    public SimpleTestUsernamePasswordAuthenticationHandler() {
        log.warn(this.getClass().getName()
                + " is only to be used in a testing environment.  NEVER enable this in a production environment.");
    }

    public boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) {
        final String username = credentials.getUsername();
        final String password = credentials.getPassword();

     //用户名和密码都不为空,并且相等返回true
        if (StringUtils.hasText(username) && StringUtils.hasText(password)
            && username.equals(getPasswordEncoder().encode(password))) {
            log.debug("User [" + username
                    + "] was successfully authenticated.");
            return true;
        }

        log.debug("User [" + username + "] failed authentication");

        return false;
    }
}
  • AbstractJdbcUsernamePasswordAuthenticationHandler

  AbstractJdbcUsernamePasswordAuthenticationHandler处理是jdbc用户名密码验证接口的父类,先讲它为后面讲其他两个打下基础,也更好去理解。通过源码知道,该Handler只是提供了设置DataSource的方式,也就是提供了连接数据库的方式,通过datasource新建一个JdbcTemplate,就可以方便的查询数据库。源码如下

public abstract class AbstractJdbcUsernamePasswordAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {

    @NotNull
    private SimpleJdbcTemplate jdbcTemplate;
    
    @NotNull
    private DataSource dataSource;

    /**
     * Method to set the datasource and generate a JdbcTemplate.
     * 
     * @param dataSource the datasource to use.
     */
    public final void setDataSource(final DataSource dataSource) {
        this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
        this.dataSource = dataSource;
    }

    /**
     * Method to return the jdbcTemplate
     * 
     * @return a fully created JdbcTemplate.
     */
    protected final SimpleJdbcTemplate getJdbcTemplate() {
        return this.jdbcTemplate;
    }
    
    protected final DataSource getDataSource() {
        return this.dataSource;
    }
}

  当需要验证用户名密码的时候,会调用父类(AbstractUsernamePasswordAuthenticationHandler)的doAuthentication方法,也就会调用子类的authenticateUsernamePasswordInternal方法,代码如下,下面省略了部分代码

public abstract class AbstractUsernamePasswordAuthenticationHandler extends
    AbstractPreAndPostProcessingAuthenticationHandler {
  /**
     * Method automatically handles conversion to UsernamePasswordCredentials
     * and delegates to abstract authenticateUsernamePasswordInternal so
     * subclasses do not need to cast.
     */
    protected final boolean doAuthentication(final Credentials credentials)
        throws AuthenticationException {
        return authenticateUsernamePasswordInternal((UsernamePasswordCredentials) credentials);
    }

    /**
     * Abstract convenience method that assumes the credentials passed in are a
     * subclass of UsernamePasswordCredentials.
     * 
     * @param credentials the credentials representing the Username and Password
     * presented to CAS
     * @return true if the credentials are authentic, false otherwise.
     * @throws AuthenticationException if authenticity cannot be determined.
     */
    protected abstract boolean authenticateUsernamePasswordInternal(
        final UsernamePasswordCredentials credentials)
        throws AuthenticationException;
}
  • SearchModeSearchDatabaseAuthenticationHandler

  这个类(继承了AbstractJdbcUsernamePasswordAuthenticationHandler)只需要提供表名、用户名字段、密码字段,通过替换SQL字符串,来查询数据库里的用户名和密码

public class SearchModeSearchDatabaseAuthenticationHandler extends
    AbstractJdbcUsernamePasswordAuthenticationHandler implements InitializingBean {

    private static final String SQL_PREFIX = "Select count('x') from ";

    @NotNull
    private String fieldUser;

    @NotNull
    private String fieldPassword;

    @NotNull
    private String tableUsers;

    private String sql;

    protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {
        final String transformedUsername = getPrincipalNameTransformer().transform(credentials.getUsername());
        final String encyptedPassword = getPasswordEncoder().encode(credentials.getPassword());

        final int count = getJdbcTemplate().queryForInt(this.sql,
           transformedUsername, encyptedPassword);

        return count > 0;
    }

    public void afterPropertiesSet() throws Exception {
        this.sql = SQL_PREFIX + this.tableUsers + " Where " + this.fieldUser
        + " = ? And " + this.fieldPassword + " = ?"; 
    }

    /**
     * @param fieldPassword The fieldPassword to set.
     */
    public final void setFieldPassword(final String fieldPassword) {
        this.fieldPassword = fieldPassword;
    }

    /**
     * @param fieldUser The fieldUser to set.
     */
    public final void setFieldUser(final String fieldUser) {
        this.fieldUser = fieldUser;
    }

    /**
     * @param tableUsers The tableUsers to set.
     */
    public final void setTableUsers(final String tableUsers) {
        this.tableUsers = tableUsers;
    }
}
  • QueryDatabaseAuthenticationHandler

  这个类(也继承了AbstractJdbcUsernamePasswordAuthenticationHandler),只需要提供预编译型的查询密码的SQL,通过jdbcTemplate可以查询出用户名对应的密码即可,源代码如下:

public class QueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {

    @NotNull
    private String sql;

    protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {
        final String username = getPrincipalNameTransformer().transform(credentials.getUsername());
        final String password = credentials.getPassword();
        final String encryptedPassword = this.getPasswordEncoder().encode(
            password);
        
        try {
            final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username);
            return dbPassword.equals(encryptedPassword);
        } catch (final IncorrectResultSizeDataAccessException e) {
            // this means the username was not found.
            return false;
        }
    }

    /**
     * @param sql The sql to set.
     */
    public void setSql(final String sql) {
        this.sql = sql;
    }
}
posted @ 2017-05-16 11:36  風之殤  阅读(502)  评论(0)    收藏  举报