Spring-Security自动配置类概述

概述

spring-boot-starter-security和spring其它系列的starter一样,依赖spring-boot-autoconfigure,根据其META-INF下的spring.factories加载自动配置类,然后进行一系列的初始化配置。查看spring.factories下的EnableAutoConfiguration和spring-security相关的一共有以下几种自动配置

1
2
3
4
5
6
SecurityAutoConfiguration
SecurityRequestMatcherProviderAutoConfiguration
UserDetailsServiceAutoConfiguration
SecurityFilterAutoConfiguration
ReactiveSecurityAutoConfiguration
ReactiveUserDetailsServiceAutoConfiguration

下面对这些自动配置类进行简单的分析

SecurityAutoConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
// 类路径下存在DefaultAuthenticationEventPublisher时才进行装配
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
// 配置参数
@EnableConfigurationProperties(SecurityProperties.class)
// 导入SpringBootWebSecurityConfiguration,WebSecurityEnablerConfiguration和SecurityDataConfiguration配置类
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {

@Bean
// 类路径下存在AuthenticationEventPublisher时才进行装配
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(
ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}

}

SecurityRequestMatcherProviderAutoConfiguration

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
@Configuration
// 类路径下存在RequestMatcher时才进行装配
@ConditionalOnClass({ RequestMatcher.class })
// 基于servlet的Web应用程序才进行装配
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class SecurityRequestMatcherProviderAutoConfiguration {

@Configuration
// 类路径下存在DispatcherServlet时才进行装配
@ConditionalOnClass(DispatcherServlet.class)
// spring容器中存在HandlerMappingIntrospector实例时才进行装配
@ConditionalOnBean(HandlerMappingIntrospector.class)
public static class MvcRequestMatcherConfiguration {

@Bean
// 类路径下存在DispatcherServlet时才进行装配
@ConditionalOnClass(DispatcherServlet.class)
public RequestMatcherProvider requestMatcherProvider(
HandlerMappingIntrospector introspector) {
return new MvcRequestMatcherProvider(introspector);
}

}

@Configuration
// 类路径下存在ResourceConfig时才进行装配
@ConditionalOnClass(ResourceConfig.class)
// 类路径下不存在org.springframework.web.servlet.DispatcherServlet时才进行装配
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
// spring容器中存在JerseyApplicationPath实例时才进行装配
@ConditionalOnBean(JerseyApplicationPath.class)
public static class JerseyRequestMatcherConfiguration {

@Bean
public RequestMatcherProvider requestMatcherProvider(
JerseyApplicationPath applicationPath) {
return new JerseyRequestMatcherProvider(applicationPath);
}

}

}

UserDetailsServiceAutoConfiguration

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
@Configuration
// 类路径下存在AuthenticationManager时才进行装配
@ConditionalOnClass(AuthenticationManager.class)
// spring容器中存在ObjectPostProcessor实例时才进行装配
@ConditionalOnBean(ObjectPostProcessor.class)
// spring容器中同时不存在AuthenticationManager,AuthenticationProvider,UserDetailsService实例时才进行装配
@ConditionalOnMissingBean({ AuthenticationManager.class, AuthenticationProvider.class,
UserDetailsService.class })
public class UserDetailsServiceAutoConfiguration {

private static final String NOOP_PASSWORD_PREFIX = "{noop}";

private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern
.compile("^\\{.+}.*$");

private static final Log logger = LogFactory
.getLog(UserDetailsServiceAutoConfiguration.class);

@Bean
@ConditionalOnMissingBean(
type = "org.springframework.security.oauth2.client.registration.ClientRegistrationRepository")
@Lazy
public InMemoryUserDetailsManager inMemoryUserDetailsManager(
SecurityProperties properties,
ObjectProvider<PasswordEncoder> passwordEncoder) {
SecurityProperties.User user = properties.getUser();
List<String> roles = user.getRoles();
return new InMemoryUserDetailsManager(User.withUsername(user.getName())
.password(getOrDeducePassword(user, passwordEncoder.getIfAvailable()))
.roles(StringUtils.toStringArray(roles)).build());
}

private String getOrDeducePassword(SecurityProperties.User user,
PasswordEncoder encoder) {
String password = user.getPassword();
if (user.isPasswordGenerated()) {
logger.info(String.format("%n%nUsing generated security password: %s%n",
user.getPassword()));
}
if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
return password;
}
return NOOP_PASSWORD_PREFIX + password;
}

}

SecurityFilterAutoConfiguration

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
@Configuration
// 基于servlet的Web应用程序才进行装配
@ConditionalOnWebApplication(type = Type.SERVLET)
// 将SecurityProperties注册为bean
@EnableConfigurationProperties(SecurityProperties.class)
// 类路径下同时存在AbstractSecurityWebApplicationInitializer,SessionCreationPolicy时才进行装配
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class,
SessionCreationPolicy.class })
// 在SecurityAutoConfiguration准备后才进行装配
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {

private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;

@Bean
// spring容器中存在名称为springSecurityFilterChain的实例时才进行装配
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}

private EnumSet<DispatcherType> getDispatcherTypes(
SecurityProperties securityProperties) {
if (securityProperties.getFilter().getDispatcherTypes() == null) {
return null;
}
return securityProperties.getFilter().getDispatcherTypes().stream()
.map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors
.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
}

}

剩下的ReactiveSecurityAutoConfiguration和ReactiveUserDetailsServiceAutoConfiguration是spring5新增的reactive非阻塞的web框架中的配置类,不在本次的讨论之中,就不做分析了。

总结

本章主要分析了spring-security在项目启动时主要加载了哪些自动配置类,从spring-boot-autoconfigure的META-INF下的spring.factories文件中,我们可以发现,在项目启动时主要是加载SecurityAutoConfiguration
SecurityRequestMatcherProviderAutoConfiguration、UserDetailsServiceAutoConfiguration、
SecurityFilterAutoConfiguration这四个配置类,接下来的文章我们将围绕这四个配置类分析spring-security究竟为我们做了哪些自动配置。