Spring Security用户认证成功失败源码分析

通常用户登录成功或者失败之后要做一些处理,比如日志记录、数据初始化等等;Spring中提供了事件及监听器,而Spring Security很好的运用了这一特点,框架中用了很多的事件来处理,要想很好的控制这些,先熟悉下源码,要知其所以然。

ProviderManager类分析

已经对该类进行过分析,侧重点不太一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
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类型,它是一个接口,里面有如下两个方法:

1
2
3
4
5
6
7
public interface AuthenticationEventPublisher {
//发布认证成功事件
void publishAuthenticationSuccess(Authentication authentication);
//发布认证失败事件
void publishAuthenticationFailure(AuthenticationException exception,
Authentication authentication);
}

我们在ProviderManager类中看到eventPublisher的初始化值是一个NullEventPublisher对象,但是NullEventPublisher类中没有做任何的处理,所以推测肯定有其它地方对eventPublisher进行初始化,ProviderManager类中有一个setAuthenticationEventPublisher方法:

1
2
3
4
5
public void setAuthenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
}

上面的方法就是初始化事件发布对象的入口,这个方法是由AuthenticationManagerBuilder类来进行初始化的

AuthenticationManagerBuilder类分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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对象及初始化方法,即:

1
2
3
4
5
6
7
8
private AuthenticationEventPublisher eventPublisher;

public AuthenticationManagerBuilder authenticationEventPublisher(
AuthenticationEventPublisher eventPublisher) {
Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
this.eventPublisher = eventPublisher;
return this;
}

那么是由哪个类调用AuthenticationManagerBuilderauthenticationEventPublisher初始化方法,接下来看下WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter类分析

1
2
3
4
5
6
7
8
9
10
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方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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中的事件发布采用倒叙的方式已经将源码分析完毕

本文转载参考 原文 并加以调试

Powered by AppBlog.CN     浙ICP备14037229号

Copyright © 2012 - 2020 APP开发技术博客 All Rights Reserved.

访客数 : | 访问量 :