我们在使用SSO单点登录的时候不只是验证一下用户名和密码是否一致,有时候还需要验证一些别的校验,那么这一张讲一下如何自定义验证器。
自定义验证很重要,因为我们后续的很多功能,都是基于自定义验证。
CAS服务器的org.apereo.cas.authentication.AuthenticationManager负责基于提供的凭证信息进行用户认证。与Spring Security很相似,实际的认证委托给了一个或多个实现了org.apereo.cas.authentication.AuthenticationHandler接口的处理类。在cas的认证过程中逐个执行authenticationHandlers中配置的认证管理,直到有一个成功为止。
CAS内置了一些AuthenticationHandler实现类,如下图所示,QueryDatabaseAuthenticationHandler中提供了基于jdbc的用户认证。
如果需要实现自定义登录,只需要实现org.apereo.cas.authentication.AuthenticationHandler接口即可。当然也可以利用已有的实现,比如创建一个继承自org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler的类。下面我们就来实现自定义验证。
自定义验证
pom添加依赖
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-authentication</artifactId>
<version>${cas.version}</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-webflow</artifactId>
<version>${cas.version}</version>
</dependency>
自定义验证类,继承AbstractUsernamePasswordAuthenticationHandler
在此只验证是不是admin用户,其他用户不让登录。
package com.wangsaichao.cas.custom;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import javax.security.auth.login.AccountNotFoundException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
/**
* @author: wangsaichao
* @date: 2018/7/21
* @description: 自定义验证器
*/
public class MyAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler{
public MyAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
if("admin".equals(credential.getUsername())){
return createHandlerResult(credential,
this.principalFactory.createPrincipal(credential.getUsername()),
new ArrayList<>(0));
}else{
throw new AccountNotFoundException("必须是admin用户才允许通过");
}
}
}
注册验证器
package com.wangsaichao.cas.config;
import com.wangsaichao.cas.custom.MyAuthenticationHandler;
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.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: wangsaichao
* @date: 2018/7/21
* @description: 注册验证器
*/
@Configuration("myAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class MyAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
/**
* 将自定义验证器注册为Bean
* @return
*/
@Bean
public AuthenticationHandler myAuthenticationHandler() {
MyAuthenticationHandler handler = new MyAuthenticationHandler(MyAuthenticationHandler.class.getSimpleName(), servicesManager, new DefaultPrincipalFactory(), 1);
return handler;
}
/**
* 注册验证器
* @param plan
*/
@Override
public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(myAuthenticationHandler());
}
}
加载该配置类
在resources\META-INF\spring.factories中配置该类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.wangsaichao.cas.config.MyAuthenticationConfiguration
测试
使用test/123456登录注意:
结果可能很奇怪,使用test竟然可以登录,那我们自定义验证器有什么用呢?上面我们说了:在cas的认证过程中逐个执行authenticationHandlers中配置的认证管理,直到有一个成功为止。也就是说在执行的过程中,肯定有一个authenticationHandlers执行成功了,通过debug发现,PolicyBasedAuthenticationManager中如下截图:
为什么会出现这种情况,还记得我们上一篇博客中配置jdbc验证,以及密码加盐处理,在application.properties中有配置以下两中开头的配置:
cas.authn.jdbc.query[0].* 和 cas.authn.jdbc.encode[0].*
分别对应了:QueryDatabaseAuthenticationHandler 和 QueryAndEncodeDatabaseAuthenticationHandler 其中使用test/123456用户登录QueryAndEncodeDatabaseAuthenticationHandler这个处理器执行成功了,所以就登录成功。
所以我们在使用自定义验证器的时候,查询数据库操作,要自己实现,而不能使用cas自带的,包括密码加密等。
再次测试
将application.properties中jdbc相关 和 密码加盐相关配置注释, 再次使用admin 和 test 账户测试。密码随便写,因为并没有走数据库校验。
admin可以登录。
test不可以登录,认证失败。