日期:2014-05-16  浏览次数:20533 次

Spring JDBC实践之--Yale CAS登录模块的一个典型的客户化
  项目使用Yale CAS+Spring Security实现单点登录以及权限验证
需要对Yale CAS的登录模块进行一下改动,如果用户输入帐号密码失败次数超过3次的时候,要把帐号锁定,这样要等管理员解锁以后才可以再次登录这个帐号。
Yale CAS的登录是个典型的密码验证模块,不能提供上述需求,这就需要对CAS的登录部分做些改动
(暂且不考虑这个需求是不是合理,单纯从技术角度来实现这个需求)
具体分析如下:

  • 1. 一个帐户需要多记录两个属性:密码输错的次数以及帐户是否可登录;
  • 2. 用户提交登录申请的时候,先检验该帐户是否可登录,如果是,继续进行下面的,如果否,返回登录失败信息;
  • 3. 校验用户帐户和密码,如果密码正确,返回登录成功,如果密码错误,继续进行下面的;
  • 4. 该帐户的密码输错次数加1,并比较现在的输错次数是否小于3(可配置),如果是,返回登录失败信息,如果否,继续进行下面的;
  • 5. 将帐户是否可登录属性设置为否,返回登录失败信息。


综合上述分析,实现如下:
  • 1. 数据库中设计帐户信息时加两个字段failureTimes和isValid来记录错误登录次数和是否被锁定
  • 2. 重写CAS的密码校验模块,
package com.cas;

import org.inspektr.common.ioc.annotation.NotNull;
import org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.handler.BadPasswordAuthenticationException;
import org.jasig.cas.authentication.handler.UnknownUsernameAuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

/**
 * Class that if provided a query that returns a password (parameter of query must be username) will compare that
 * password to a translated version of the password provided by the user. If they match, then authentication succeeds.
 * Default password translator is plaintext translator.
 *
 * @Date 2009-5-23
 */
public class JdbcUsernamePasswordAuthHandlerImpl extends AbstractJdbcUsernamePasswordAuthenticationHandler {

    // it's better to move below properties to external configure file, for example 'maxFailureTimes'
    private static final String QUERY_USER_SQL = "select * from user_info where username = ?";
    private static final String FAILURE_TRIGGER_SQL = "update user_info set failureTimes = ? where username = ?";
    private static final String LOCK_USER_SQL = "update user_info set failureTimes = ?, isValid = ? where username = ?";

    @NotNull
    private String maxFailureTimes;

    /**
     * @param paraMaxFailureTimes
     *            the maxFailureTimes to set
     */
    public void setMaxFailureTimes(String paraMaxFailureTimes) {
        this.maxFailureTimes = paraMaxFailureTimes;
    }

    /**
     * authenticate username password internal
     *
     * @param credentials
     *            credentials
     * @throws AuthenticationException
     *             AuthenticationException
     * @return true if user login success
     * @see org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler
     *      #authenticateUsernamePasswordInternal(org.jasig.cas.authentication.principal.UsernamePasswordCredentials)
     */
    @Override
    protected boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials)
        throws AuthenticationException {
        final String username = credentials.getUsername();
        final String password = credentials.getPassword();
        JdbcTemplate template = new JdbcTemplate(getDataSource());

        try {
            // get user info by username, if no result found, auto throw IncorrectResultSizeDataAccessException
            UserInfo userInfo = (UserInfo) template.queryForObject(QUERY_USER_SQL, new String[]{username},
                new BeanPropertyRowMapper(UserInfo.class));
            // check user lock
            if (!"Y".equalsIgnoreCase(userInfo.getIsValid())) {
                // means user was locked
                throw new AccountLockedException();
            } else if (password.equals(userInfo.getPassword())) {
                // means correct username/password, login success return true