CAS 5.3版本自定义JDBC认证策略
CAS 5.3版本自定义JDBC认证策略
CAS 官方文档是介绍基于配置实现 jdbc 认证的,不过有时候认证逻辑比较复杂,比如:数据库存储的密码是通过动态盐加密的,此时我们需要通过自定义认证策略的方式实现认证
pom 文件引入依赖
<!--开启cas server的rest支持-->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-rest</artifactId>
<version>${cas.version}</version>
</dependency>
<!-- 数据库 -->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc-drivers</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<!--自定义认证-->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-authentication-api</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-configuration-api</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-generic</artifactId>
<version>${cas.version}</version>
</dependency>
注意依赖添加位置(在标记处下面添加新的依赖):
继承 AbstractPreAndPostProcessingAuthenticationHandler 抽象类,自定义认证逻辑:
package com.povison.handler;
import com.povison.domain.entity.SysUser;
import com.povison.util.PasswordUtil;
import org.apache.shiro.authc.AccountException;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.MessageDescriptor;
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.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.util.ObjectUtils;
import javax.security.auth.login.FailedLoginException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 自定义认证处理器
*
* @author Y-wee
*/
public class CustomAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
public CustomAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
/**
* 执行身份验证的详细信息并在成功时返回身份验证处理程序结果
*
* @param credential
* @return
* @throws GeneralSecurityException
* @throws PreventedException
*/
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
UsernamePasswordCredential usernamePasswordCredential = (UsernamePasswordCredential) credential;
String username = usernamePasswordCredential.getUsername();
String password = usernamePasswordCredential.getPassword();
/*
构建数据库驱动连接池
*/
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("123456");
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
String sql = "SELECT salt,password FROM sys_user WHERE status=1 and username = ?";
SysUser sysUser = (SysUser) jdbcTemplate.queryForObject(sql, new Object[]{username}, new BeanPropertyRowMapper(SysUser.class));
if (ObjectUtils.isEmpty(sysUser)) {
throw new AccountException("Sorry, username not found!");
}
/*
* 将密码加密后与数据库查询出来的密码比较,验证密码是否正确
*/
String encryptPassword=PasswordUtil.encrypt(username, password, sysUser.getSalt());
if (!sysUser.getPassword().equals(encryptPassword)) {
throw new FailedLoginException("Sorry, password not correct!");
} else {
final List<MessageDescriptor> list = new ArrayList<>();
return createHandlerResult(usernamePasswordCredential,
this.principalFactory.createPrincipal(username, Collections.emptyMap()), list);
}
}
/**
* Determines whether the handler has the capability to authenticate the given credential. In practical terms,
* the {@link #authenticate(Credential)} method MUST be capable of processing a given credential if
* {@code supports} returns true on the same credential.
*
* @param credential The credential to check.
* @return True if the handler supports the Credential, false otherwise.
*/
@Override
public boolean supports(Credential credential) {
return credential instanceof UsernamePasswordCredential;
}
}
新增一个配置类,实现 AuthenticationEventExecutionPlanConfigurer 接口:
package com.povison.config;
import com.povison.handler.CustomAuthenticationHandler;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.PrePostAuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
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.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自定义用户认证的实体类注册配置类
*
* @author Y-wee
*/
@Configuration("CustomAuthConfig")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomAuthConfig implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
/**
* 配置 PrePostAuthenticationHandler Bean
*
* @return
*/
@Bean
public PrePostAuthenticationHandler getAuthenticationHandler() {
return new CustomAuthenticationHandler(CustomAuthenticationHandler.class.getName(),
servicesManager, new DefaultPrincipalFactory(), 1);
}
/**
* configure the plan.
*
* @param plan
*/
@Override
public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(getAuthenticationHandler());
}
}
在 resource 资源文件夹下新建 META-INF/spring.factories 文件,添加自动配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.povison.config.CustomAuthConfig
将注释 application.properties 中配置的默认账号信息注释
#cas.authn.accept.users=casuser::Mellon
重启服务,即可通过数据库存储的账号信息登录了
记得快乐