SSO单点登录的时候不只是验证一下用户名和密码是否一致,有时候还需要验证一些别的校验,之就需要我们进行自定义验证。具体查看官网文档说明。
https://apereo.github.io/cas/5.2.x/installation/Configuring-Custom-Authentication.html#overview
CAS服务器的org.apereo.cas.authentication.AuthenticationManager负责基于提供的凭证信息进行用户认证。实际的认证委托给了一个或多个实现了org.apereo.cas.authentication.AuthenticationHandler接口的处理类。在cas的认证过程中逐个执行authenticationHandlers中配置的认证管理,直到有一个成功为止。 CAS内置了一些AuthenticationHandler实现类 。
身份验证处理程序本身定义框架。这是核心主要组件,其作用是声明对给定类型的凭证的支持,然后尝试验证它并生成成功的结果。所有处理程序扩展的核心父组件是AuthenticationHandler
接口。
因而自定义验证器继承其中任意个重写里面相应的方法或者实现AuthenticationHandler接口也可以。并且 应为是自定义的登陆控制器,所以我们需要重写数据库链接,如果不重写,cas还是默认使用的是在application.properties中有配置以下两中开头的配置: cas.authn.jdbc.query[0].* 和 cas.authn.jdbc.encode[0].* 分别对应了:QueryDatabaseAuthenticationHandler 和 QueryAndEncodeDatabaseAuthenticationHandler .
重写数据库链接方式:
修改application.properties配置链接数据库方式:
##########################################################JDBC验证##############################################################################
#
##Query Database Authentication 数据库查询校验用户名开始
##查询账号密码sql,必须包含密码字段
##数据库查询用户名语句,其中user_info对应表名,username对应登录账号的字段名
#cas.authn.jdbc.query[0].sql=SELECT * FROM user_info WHERE username =?
##指定上面的sql查询字段名(必须)
#cas.authn.jdbc.query[0].fieldPassword=password
##指定过期字段,1为过期,若过期不可用(可选)
##cas.authn.jdbc.query[0].fieldExpired=expired
##为不可用字段段,1为不可用,需要修改密码(可选)
##cas.authn.jdbc.query[0].fieldDisabled=disabled
##数据库方言hibernate的
#cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
##数据库驱动
#cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
##数据库连接
#cas.authn.jdbc.query[0].url=jdbc:mysql://ddd:3306/test?useUnicode=true&characterEncoding=UTF-8
##数据库用户名
#cas.authn.jdbc.query[0].user=root
##数据库密码
#cas.authn.jdbc.query[0].password=ddd
##默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
##cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT 自定义加密策略
#cas.authn.jdbc.query[0].passwordEncoder.type=com.cas.passwordEncode.MyEncoder
#cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
#cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
##Query Database Authentication 数据库查询校验用户名结束
###########################################################JDBC验证 加盐##############################################################################
#
##加密迭代次数
#cas.authn.jdbc.encode[0].numberOfIterations=2
##该列名的值可替代上面的值,但对密码加密时必须取该值进行处理
#cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
##盐值固定列 盐值
#cas.authn.jdbc.encode[0].saltFieldName=username
##静态盐值
#cas.authn.jdbc.encode[0].staticSalt=.
#cas.authn.jdbc.encode[0].sql=SELECT * FROM user_info WHERE username =?
##对处理盐值后的算法
#cas.authn.jdbc.encode[0].algorithmName=MD5
#cas.authn.jdbc.encode[0].passwordFieldName=password
#cas.authn.jdbc.encode[0].expiredFieldName=expired
#cas.authn.jdbc.encode[0].disabledFieldName=disabled
##数据库连接
#cas.authn.jdbc.encode[0].url=jdbc:mysql://ddd:3306/test?useUnicode=true&characterEncoding=UTF-8
#cas.authn.jdbc.encode[0].dialect=org.hibernate.dialect.MySQL5Dialect
#cas.authn.jdbc.encode[0].driverClass=com.mysql.jdbc.Driver
#cas.authn.jdbc.encode[0].user=root
#cas.authn.jdbc.encode[0].password=ddd
#注意:如果两种方式都配置的话,默认先用普通MD5验证,如果验证失败,打印异常日志,然后再使用加盐方式验证。
##########################################################spring boot自动配置数据原##############################################################################
#下面所是数据源,让spring boot自动配置,默认cas屏蔽了spring boot自动配置,下面的代码中我们在spring boot扫描的目录中开启这个配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://dddd:3306/test?useUnicode=true&characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=dddd
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
#连接池配置
##初始化连接:连接池启动时创建的初始化连接数量
spring.datasource.dbcp2.initial-size=5
#最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
spring.datasource.dbcp2.max-active=1000
#最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
spring.datasource.dbcp2.max-idle=100
#从连接池获取一个连接时,最大的等待时间,设置为-1时,如果没有可用连接,连接池会一直无限期等待,直到获取到连接为止。
#如果设置为N(毫秒),则连接池会等待N毫秒,等待不到,则抛出异常
spring.datasource.dbcp2.max-wait-millis=60000
#通过这个池创建连接的默认自动提交状态。如果不设置,则setAutoCommit 方法将不被调用
spring.datasource.dbcp2.default-auto-commit=true
#通过这个池创建连接的默认只读状态。如果不设置,则setReadOnly 方法将不被调用。(部分驱动不支持只读模式,如:Informix)
spring.datasource.dbcp2.default-read-only=false
#指明在从池中租借对象时是否要进行验证有效,如果对象验证失败,则对象将从池子释放,然后我们将尝试租借另一个
spring.datasource.dbcp2.test-on-borrow=true
#自定义属性 这些属性我们在jdbc验证的时候需要
#用户名,密码查询
user.password.query.sql = SELECT * FROM user_info WHERE username =?
修改成spring boot默认配置数据源方式。
自定义登陆验证:
package com.cas.authentication;
import com.cas.passwordEncode.MyEncoder;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.dao.DataAccessException;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.HandlerResult;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.ResultSetExtractor;
import javax.security.auth.login.AccountNotFoundException;
import java.security.GeneralSecurityException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Project casspringboot
* @Package com.cas
* @ClassName MyUserNamePasswordAuthenticationHandler
* @Descripition TODO
* @Author able
* @Date 2019/6/13 15:55
* @Version 1.0
**/
public class MyUserNamePasswordAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
/**
* 为了符合cacheService key的名称定义格式。
*/
public static final String CSUFFIX_USERLOGIN = "_2ae941e8-21e5-4013-9568-4dcee975333a";
/**
* 根据用户名和密码查询sql语句
*/
@Value("${user.password.query.sql}")
private String userQuerySql;
/**
* jdbc模板
*/
@Autowired
private JdbcTemplate jdbcTemplate;
public MyUserNamePasswordAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
protected HandlerResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
if("admin".equals(credential.getUsername())){
List<Userinfo> list = new ArrayList();
jdbcTemplate.query(userQuerySql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, credential.getUsername());
}
}, new ResultSetExtractor() {
public Object extractData(ResultSet rs) throws SQLException, DataAccessException {
while (rs.next()) {
Userinfo u = new Userinfo();
u.setId(rs.getInt("id"));
u.setUsername(rs.getString("username"));
u.setPassword(rs.getString("password"));
u.setName(rs.getString("name"));
list.add(u);
}
return list;
}
});
if(CollectionUtils.isNotEmpty(list)){
Userinfo o = list.get(0);
System.out.println(o);
}
//TODO 相关业务胜率
return createHandlerResult(credential,
this.principalFactory.createPrincipal(credential.getUsername()),
new ArrayList<>(0));
}else{
throw new AccountNotFoundException("必须是admin用户才允许通过");
}
}
}
注册验证器:
package com.cas.authentication;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/*
* @Project casspringboot
* @Package com.cas.authentication
* @ClassName AuthenticationConfig
* @Descripition 注册自定义验证器
* @Author able
* @Date 2019/6/13 17:17
* @Version 1.0
*/
//增加了@Import({DataSourceAutoConfiguration.class}),开启Spring Boot数据源自动配置,这样才可以使用使用jdbcTemplate
@Import({DataSourceAutoConfiguration.class})
@Configuration("myAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class AuthenticationConfig implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Autowired
@Qualifier("jdbcPrincipalFactory")
public PrincipalFactory jdbcPrincipalFactory;
/*
* 注册验证器
*
* @return
*/
@Bean
public AuthenticationHandler customAuthenticationHandler() {
//优先验证
MyUserNamePasswordAuthenticationHandler handler = new MyUserNamePasswordAuthenticationHandler(MyUserNamePasswordAuthenticationHandler.class.getSimpleName(), servicesManager, new DefaultPrincipalFactory(), 1);
return handler;
}
//注册自定义认证器
@Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(customAuthenticationHandler());
}
}
加载该配置类
在resources\META-INF\spring.factories中配置该类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apereo.cas.config.CasEmbeddedContainerTomcatConfiguration,\
com.cas.authentication.AuthenticationConfig
测试: