Security中的认证事件发布器
Posted agony-wxl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Security中的认证事件发布器相关的知识,希望对你有一定的参考价值。
AuthenticationEventPublisher
AuthenticationEventPublisher
从上图我们能看出AuthenticationEventPublisher (认证事件发布器)被Security定义为接口的形式,它定义了Spring Security中用户授权成功或失败的通知机制.题外话
对于认证事件发布器是Security是如何加载并使用的,小伙伴们是否有许多的问号?在这里,仅仅告诉小伙伴们我的师傅(大数据大佬)曾经告诉我一个道理,作为我们这一行,看所有代码都需要带着问题去探究,Spring为什么要如此定义它,定义它以后又是如何加载并使用的,这些问题,其实值得我们思考和学习!
运行机制
对于AuthenticationEventPublisher 的运行机制,我进行了深入的探究,接下来带你们来一看究竟
上图明确的表示了Spring Security是如何加载AuthenticationEventPublisher 的。有疑问的小伙伴可以自行代码调试!
关于 AbstractApplicationContext 如有不懂的可以看 AbstractApplicationContext 源码分析 这篇文章
Spring Security如何使用认证事件发布器
- SecurityAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class,WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
SecurityAutoConfiguration 将 DefaultAuthenticationEventPublisher 作为默认的 AuthenticationEventPublisher 注入Spring Ioc容器。
- DefaultAuthenticationEventPublisher
public class DefaultAuthenticationEventPublisher implements AuthenticationEventPublisher,
ApplicationEventPublisherAware {
private final Log logger = LogFactory.getLog(getClass());
private ApplicationEventPublisher applicationEventPublisher;
private final HashMap<String, Constructor<? extends AbstractAuthenticationEvent>> exceptionMappings = new HashMap<>();
public DefaultAuthenticationEventPublisher() {
this(null);
}
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 | InvocationTargetException | InstantiationException 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());
}
}
}
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
@SuppressWarnings({ "unchecked" })
public void setAdditionalExceptionMappings(Properties additionalExceptionMappings) {
Assert.notNull(additionalExceptionMappings,
"The exceptionMappings object must not be null");
for (Object exceptionClass : additionalExceptionMappings.keySet()) {
String eventClass = (String) additionalExceptionMappings.get(exceptionClass);
try {
Class<?> clazz = getClass().getClassLoader().loadClass(eventClass);
Assert.isAssignable(AbstractAuthenticationFailureEvent.class, clazz);
addMapping((String) exceptionClass,
(Class<? extends AbstractAuthenticationFailureEvent>) clazz);
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Failed to load authentication event class "
+ eventClass);
}
}
}
private void addMapping(String exceptionClass,
Class<? extends AbstractAuthenticationFailureEvent> eventClass) {
try {
Constructor<? extends AbstractAuthenticationEvent> constructor = eventClass
.getConstructor(Authentication.class, AuthenticationException.class);
exceptionMappings.put(exceptionClass, constructor);
}
catch (NoSuchMethodException e) {
throw new RuntimeException("Authentication event class "
+ eventClass.getName() + " has no suitable constructor");
}
}
}
查看源码我们发现,该类内置了一个HashMap<String, Constructor<? extends AbstractAuthenticationEvent>> 用于维护认证异常处理和对应异常事件处理逻辑的映射关系,比如坏的凭证异常 BadCredentialsException 对应认证失败,凭据错误事件AuthenticationFailureBadCredentialsEvent 也就是说当发生不同认证的异常时,Security会采用不同的处理策略。
以上是关于Security中的认证事件发布器的主要内容,如果未能解决你的问题,请参考以下文章