环境说明:IDEA+JDK7+TOMCAT8 注意不要用JDK8,出现冲突需要更新额外jar包版本(不是教程重点)

一、修改登录验证方案

修改deployerConfigContext.xml primaryAuthenticationHandler有多个实现类:

  • org.jasig.cas.authentication.LdapAuthenticationHandler
  • org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler
  • org.jasig.cas.adaptors.x509.authentication.handler.support.X509CredentialsAuthenticationHandler
  • org.jasig.cas.support.spnego.authentication.handler.support.JCIFSSpnegoAuthenticationHandler
  • org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler

我们选择自己写一个SaaSAuthenticationHandler 继承org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler

  1. <!--| TODO: Replace this component with one suitable for your enviroment.-->
  2. <!--<bean id="primaryAuthenticationHandler"
  3. class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
  4. <property name="users">
  5. <map>
  6. <entry key="casuser" value="Mellon"/>
  7. </map>
  8. </property>
  9. </bean>-->
  10. <!--重写 primaryAuthenticationHandler-->
  11. <bean id="primaryAuthenticationHandler"
  12. class="com.mac.sso.authentication.SaaSAuthenticationHandler"/>

二、代码实现

代码实现包含两个部分:

  • 重新AbstractUsernamePasswordAuthenticationHandler
  • 实现数据库查询

image.png

2.1、SaaSAuthenticationHandler

package com.mac.sso.authentication;

import cn.hutool.crypto.SecureUtil;
import com.mac.sso.bean.UserInfo;
import com.mac.sso.service.UserInfoService;
import org.jasig.cas.authentication.HandlerResult;
import org.jasig.cas.authentication.UsernamePasswordCredential;
import org.jasig.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.principal.SimplePrincipal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.security.auth.login.FailedLoginException;
import javax.servlet.http.HttpServletRequest;
import java.security.GeneralSecurityException;

/**
 * @author: byy
 * @date : 2017年11月23日 下午4:59:32
 * @Description:自定义数据库方式认证类
 */
public class SaaSAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
    @Autowired
    private UserInfoService userInfoService;

    protected HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential transformedCredential) throws GeneralSecurityException {
        //UsernamePasswordCredential参数包含了前台页面输入的用户信息
        String username = transformedCredential.getUsername().
                replaceAll(" ", "").toLowerCase();//需要剔除空格,大写转小写
        String password = transformedCredential.getPassword();
        UserInfo userInfo = userInfoService.findByUsername(username);
        System.out.println("userInfo="+userInfo);
        //1.用户名是否存在验证
        if (userInfo == null) {//用户名错误用此异常
            throw new FailedLoginException();
        }
        //生成MD5密码
        String md5password = SecureUtil.md5(password);
        //2.验证密码是否正确
        if (!md5password.equals(userInfo.getPassword())) {
            throw new FailedLoginException();
        }
        //3.返回登陆成功状态,CAS程序继续运行
        return createHandlerResult(transformedCredential, new SimplePrincipal(username), null);
    }

}

2.2、UserInfoService

package com.mac.sso.service;

import com.mac.sso.bean.UserInfo;
import com.mac.sso.dao.UserInfoDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserInfoService {

    @Autowired
    private UserInfoDAO userInfoDAO;

    public UserInfo findByUsername(String username){
        return userInfoDAO.findByUsername(username);
    }

}

2.3、UserInfoDAO

package com.mac.sso.dao;

import com.mac.sso.bean.UserInfo;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;

/**
 * @see UserInfo
 * @author byy
 */
@Repository 
public class UserInfoDAO {
    private static final Logger log = LoggerFactory.getLogger(UserInfoDAO.class);

    public UserInfo findByUsername(String username){
        Query queryObject = getSession().createQuery("from UserInfo as u where u.username=:username");
        Object u= queryObject.setString("username", username).uniqueResult();

        return (UserInfo) u;
    }

    @Resource
    private SessionFactory sessionFactory;
    private Session session;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Session getSession() {
        this.session = sessionFactory.getCurrentSession();
        return session;
    }
    public void setSession(Session session) {
        this.session = session;
    }

}

2.4、UserInfo

package com.mac.sso.bean;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * UserInfo entity. @author MyEclipse Persistence Tools
 */
@Entity
@Table(name = "user_info")
public class UserInfo implements java.io.Serializable {

    private static final long serialVersionUID = -8017178229381500057L;
    private Integer uid;
    private String username;
    private String password;
    private String realname;

    public UserInfo() {
    }

    public UserInfo(Integer uid, String username, String password, String realname) {
        this.uid = uid;
        this.username = username;
        this.password = password;
        this.realname = realname;
    }
    @Id
    @Column(name = "uid", unique = true, nullable = false, length = 50)
    public Integer getUid() {
        return uid;
    }
    public void setUid(Integer uid) {
        this.uid = uid;
    }
    @Column(name = "username", length = 100)
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    @Column(name = "password", length = 100)
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    @Column(name = "realname", length = 100)
    public String getRealname() {
        return this.realname;
    }

    public void setRealname(String realname) {
        this.realname = realname;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "uid=" + uid +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", realname='" + realname + '\'' +
                '}';
    }
}

三、数据库配置

数据库:mysql5.6 连接池:druid 脚本:user_info.sql

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET latin1 DEFAULT NULL,  
  `password` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
  `realname` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
  `state` tinyint(4) NOT NULL,

  PRIMARY KEY (`uid`) USING BTREE,
  UNIQUE KEY `UK_f2ksd6h8hsjtd57ipfq9myr64` (`username`) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of user_info
-- ----------------------------
INSERT INTO `user_info` VALUES ('1', 'admin', 'cf79ae6addba60ad018347359bd144d2', '管理员',  '0');
INSERT INTO `user_info` VALUES ('2', 'testuser', 'cf79ae6addba60ad018347359bd144d2', '测试用户', '0');

3.1、修改cas.properties

增加配置

#mysql datasource
jdbc.url=jdbc\:mysql\://localhost\:3306/shiro?useUnicode\=true&characterEncoding\=UTF8&zeroDateTimeBehavior\=convertToNull&autoReconnect\=true&failOverReadOnly\=false&maxReconnects\=10  
jdbc.username=root
jdbc.password=8888

3.2、数据源配置

增加配置文件:applicationContext-datasource.xml

<?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:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context" 
xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
                    http://www.springframework.org/schema/tx 
                    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
                    http://www.springframework.org/schema/context 
                    http://www.springframework.org/schema/context/spring-context-3.2.xsd">

   <!--单点4.0.7升级:数据源 --> 
     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
        <property name="url" value="${jdbc.url}"/>  
        <property name="username" value="${jdbc.username}"/>  
        <property name="password" value="${jdbc.password}"/>  
        <!-- 配置初始化大小、最小、最大 -->  
        <property name="initialSize" value="1"/>  
        <property name="minIdle" value="1"/>  
        <property name="maxActive" value="20"/>  
        <!-- 配置获取连接等待超时的时间 -->  
        <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的大小 -->  
        <!-- PSCache(preparedStatement)对支持游标的数据库性能提升巨大,比如说Oracle/DB2/SQL Server,在mysql下建议关闭 -->  
        <property name="poolPreparedStatements" value="false"/>  
        <property name="maxPoolPreparedStatementPerConnectionSize" value="-1"/>  
        <!-- 配置监控统计拦截的filters -->  
        <property name="filters" value="wall,mergeStat"/>  
    </bean>
     <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!-- 实体类所在的包 对包中每个类进行注解扫描 省去逐一配置-->
        <property name="packagesToScan">
            <list><value>com.mac.sso.bean</value></list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.Dialect">org.hibernate.dialect.MySQL5Dialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl">update</prop>
                <prop key="hibernate.connection.autocommit">true</prop> 
                <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop> 
            </props>
        </property>

    </bean>    
    <!--单点4.0.7升级:工厂事物 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
  <!--单点4.0.7升级:注解扫描-->  
  <context:component-scan base-package="com.mac.sso.*"/>
</beans>

3.3、pom文件修改

<!--mysql驱动-->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.8</version>
</dependency>
<!--阿里的连接池-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid</artifactId>
  <version>1.1.24</version>
</dependency>
<!--hutool轮子工具,非常好用,目前仅用来生成MD5密码-->
<!--jdk7做高支持4.*版本-->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>4.6.17</version>
</dependency>

四、效果演示

image.pngimage.png
image.png