Spring Security用户认证成功失败源码分析
通常用户登录成功或者失败之后要做一些处理,比如日志记录、数据初始化等等;Spring中提供了事件及监听器,而Spring Security很好的运用了这一特点,框架中用了很多的事件来处理,要想很好的控制这些,先熟悉下源码,要知其所以然。
ProviderManager类分析
已经对该类进行过分析,侧重点不太一样
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class<? extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
//调用用户自定义的认证处理方法
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException e) {
//重点,调用认证失败发布事件方法
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
}
catch (InternalAuthenticationServiceException e) {
//重点,调用认证失败发布事件方法
prepareException(e, authentication);
throw e;
}
catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parentResult = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = parentException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
//重点,调用认证成功发布事件方法
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
}
private void prepareException(AuthenticationException ex, Authentication auth) {
//发布失败事件
eventPublisher.publishAuthenticationFailure(ex, auth);
}
发布事件对象eventPublisher
是一个AuthenticationEventPublisher
类型,它是一个接口,里面有如下两个方法:
public interface AuthenticationEventPublisher {
//发布认证成功事件
void publishAuthenticationSuccess(Authentication authentication);
//发布认证失败事件
void publishAuthenticationFailure(AuthenticationException exception,
Authentication authentication);
}
我们在ProviderManager
类中看到eventPublisher
的初始化值是一个NullEventPublisher
对象,但是NullEventPublisher
类中没有做任何的处理,所以推测肯定有其它地方对eventPublisher
进行初始化,ProviderManager
类中有一个setAuthenticationEventPublisher
方法:
public void setAuthenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
}
上面的方法就是初始化事件发布对象的入口,这个方法是由AuthenticationManagerBuilder
类来进行初始化的
AuthenticationManagerBuilder类分析
@Override
protected ProviderManager performBuild() throws Exception {
if (!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
ProviderManager providerManager = new ProviderManager(authenticationProviders,
parentAuthenticationManager);
if (eraseCredentials != null) {
providerManager.setEraseCredentialsAfterAuthentication(eraseCredentials);
}
if (eventPublisher != null) {
//初始化发布事件
providerManager.setAuthenticationEventPublisher(eventPublisher);
}
providerManager = postProcess(providerManager);
return providerManager;
}
就是在这里对ProviderManager
类中的发布事件进行初始化的;继续查看代码发现AuthenticationManagerBuilder
类中存在eventPublisher
对象及初始化方法,即:
private AuthenticationEventPublisher eventPublisher;
public AuthenticationManagerBuilder authenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
return this;
}
那么是由哪个类调用AuthenticationManagerBuilder
的authenticationEventPublisher
初始化方法,接下来看下WebSecurityConfigurerAdapter
类
WebSecurityConfigurerAdapter类分析
public void init(final WebSecurity web) throws Exception {
final HttpSecurity http = getHttp();
web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
public void run() {
FilterSecurityInterceptor securityInterceptor = http
.getSharedObject(FilterSecurityInterceptor.class);
web.securityInterceptor(securityInterceptor);
}
});
}
上面的init方法调用了getHttp
方法
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
return http;
}
//创建DefaultAuthenticationEventPublisher实例对象
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
.postProcess(new DefaultAuthenticationEventPublisher());
//设置AuthenticationManagerBuilder对象的事件发布对象
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
authenticationBuilder.authenticationEventPublisher(eventPublisher);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
...
}
上面的源码已经注解,事件发布的真实对象就是DefaultAuthenticationEventPublisher
,它是AuthenticationEventPublisher
接口的实现,接下来看下源码分析
AuthenticationEventPublisher源码分析
public class DefaultAuthenticationEventPublisher implements AuthenticationEventPublisher,
ApplicationEventPublisherAware {
...
//添加事件子类到集合中
public DefaultAuthenticationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
addMapping(BadCredentialsException.class.getName(),
AuthenticationFailureBadCredentialsEvent.class);
addMapping(UsernameNotFoundException.class.getName(),
AuthenticationFailureBadCredentialsEvent.class);
addMapping(AccountExpiredException.class.getName(),
AuthenticationFailureExpiredEvent.class);
addMapping(ProviderNotFoundException.class.getName(),
AuthenticationFailureProviderNotFoundEvent.class);
addMapping(DisabledException.class.getName(),
AuthenticationFailureDisabledEvent.class);
addMapping(LockedException.class.getName(),
AuthenticationFailureLockedEvent.class);
addMapping(AuthenticationServiceException.class.getName(),
AuthenticationFailureServiceExceptionEvent.class);
addMapping(CredentialsExpiredException.class.getName(),
AuthenticationFailureCredentialsExpiredEvent.class);
addMapping(
"org.springframework.security.authentication.cas.ProxyUntrustedException",
AuthenticationFailureProxyUntrustedEvent.class);
}
//发布认证成功方法
public void publishAuthenticationSuccess(Authentication authentication) {
if (applicationEventPublisher != null) {
//真实发布认证成功事件
applicationEventPublisher.publishEvent(new AuthenticationSuccessEvent(
authentication));
}
}
//发布认证失败方法
public void publishAuthenticationFailure(AuthenticationException exception,
Authentication authentication) {
Constructor<? extends AbstractAuthenticationEvent> constructor = exceptionMappings
.get(exception.getClass().getName());
AbstractAuthenticationEvent event = null;
if (constructor != null) {
try {
event = constructor.newInstance(authentication, exception);
}
catch (IllegalAccessException ignored) {
}
catch (InstantiationException ignored) {
}
catch (InvocationTargetException ignored) {
}
}
if (event != null) {
if (applicationEventPublisher != null) {
//真实发布认证失败事件
applicationEventPublisher.publishEvent(event);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("No event was found for the exception "
+ exception.getClass().getName());
}
}
}
...
}
至此Spring Security中的事件发布采用倒叙的方式已经将源码分析完毕
本文转载参考 原文 并加以调试
版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/20/source-code-analysis-of-successful-and-failed-spring-security-user-authentication/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。
共有 0 条评论