1. Introduction
In previous articles, we discussed Spring Security’s user information management mechanism and password mechanism. We found that the Servlet auto-configuration related to Spring Security Starter is in spring-boot-autoconfigure-2.1.9.RELEASE (current Spring Boot version is 2.1.9.RELEASE). The path of the module org. Springframework. Boot. Autoconfigure. Security. Under the servlet. Spring-boot-autoconfigure-2.1.9. RELEASE provides the Starter auto-configuration for spring-boot-autoconfigure-2.1.9.RELEASE. Today we take a closer look at decrypting the configuration and use of Spring Security in Spring Boot.
2. Automatically configure Spring Security in Spring Boot
We can through the org. Springframework. Boot. Autoconfigure. Security. The servlet path find Spring security automatic configuration on the servlet class. Let’s get a sense of it.
2.1 SecurityAutoConfiguration
package org.springframework.boot.autoconfigure.security.servlet;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityDataConfiguration;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
/ * * * {@link EnableAutoConfiguration Auto-configuration} for Spring Security.
*
* @author Dave Syer
* @author Andy Wilkinson
* @author Madhura Bhave
* @since1.0.0 * /
@Configuration
@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 newDefaultAuthenticationEventPublisher(publisher); }}Copy the code
SecurityAutoConfiguration just as its name implies security configuration classes. The introduction (@ import) SpringBootWebSecurityConfiguration, WebSecurityEnablerConfiguration and SecurityDataConfiguration three configuration classes. Let the classes of these three modules work. Is a composite configuration and is one of the most important classes for Spring Security automatic configuration. Spring Boot autoconfiguration is often used in this way for flexible configuration purposes, This is also our study Spring Security automatic configuration of an important entry It also would DefaultAuthenticationEventPublisher SecurityAutoConfiguration as the default AuthenticationEventPublisher into the Spring IoC container. If you are familiar with the event mechanism in Spring, you know that this class is a Spring event publisher. This class has a built-in HashMap
> maintained the authentication exception handling abnormal events and the corresponding processing logic mapping relation, Such as abnormal account overdue AccountExpiredException AuthenticationFailureExpiredEvent corresponding certification overdue events, that is to say, abnormal use different strategies to deal with different authentication.
2.2 SpringBootWebSecurityConfiguration
@Configuration
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {}}Copy the code
This class is the default configuration of Spring Security for Spring Boot Servlet Web applications. The core is WebSecurityConfigurerAdapter adapter. From @ ConditionalOnMissingBean WebSecurityConfigurerAdapter. Class) we can see WebSecurityConfigurerAdapter is the core of the security configuration. By default, DefaultConfigurerAdapter will inject the Spring IoC container in securityProperty.basic_auth_order (-5), which is an empty implementation. If we need to personalization can be done through inheritance WebSecurityConfigurerAdapter. We’ll focus on this class in future posts.
2.3 WebSecurityEnablerConfiguration
@Configuration
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {}Copy the code
The configuration class will be enabled after SpringBootWebSecurityConfiguration into the Spring IoC container @ EnableWebSecurity annotation. It is simply to WebSecurityEnablerConfiguration purpose under certain conditions the activation @ EnableWebSecurity annotation. So what does this note have?
3. @ EnableWebSecurity annotation
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug(a) default false;
}
Copy the code
Annotations such as @enable * are annotations with configuration imports. Enable specific functions by importing configuration. @ EnableWebSecurity imported WebSecurityConfiguration, SpringWebMvcImportSelector, OAuth2ImportSelector and enabled @ EnableGlobalAuthentication annotation.
3.1 WebSecurityConfiguration
This configuration class, WebSecurityConfiguration, uses a WebSecurity object based on user-specified or default security configurations, You can be realized through inheritance or WebSecurityConfigurerAdapter WebSecurityConfigurer to customize the WebSecurity create a named FilterChainProxy Bean to user requests for security filters. This is the name of a named FilterChainProxy WebSecurityEnablerConfiguration BeanIds. SPRING_SECURITY_FILTER_CHAIN namely SpringSecurityFilterChain, it is a Filter that will eventually be as a Filter in the chain of Servlet filters applied to the Servlet container. The strategy for security processing is primarily the order in which filters are invoked. The WebSecurityConfiguration will eventually be applied to the system via @enableWebSecurity.
Source code analysis:
package org.springframework.security.config.annotation.web.configuration;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
/** * Spring Web Security configuration class: * 1. Use a WebSecurity object to create a FilterChainProxy object based on the security configuration to perform security filtering on user requests. * 2. Also exposed some classes such as security processor SecurityExpressionHandler SpEL expression. * *@see EnableWebSecurity
* @see WebSecurity
*
* @author Rob Winch
* @author Keesun Baik
* @since3.2 * /
@Configuration
public class WebSecurityConfiguration implements ImportAware.BeanClassLoaderAware {
private WebSecurity webSecurity;
// Whether debug mode is enabled, from annotation @enableWebSecurity property debug, the default value is false
private Boolean debugEnabled;
private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;
private ClassLoader beanClassLoader;
@Autowired(required = false)
private ObjectPostProcessor<Object> objectObjectPostProcessor;
/ * * * * the listener should be monitored DefaultAuthenticationEventPublisher some treatment strategy * /
@Bean
public static DelegatingApplicationListener delegatingApplicationListener(a) {
return new DelegatingApplicationListener();
}
/ * * * * security SpEL expression processor SecurityExpressionHandler default is a DefaultWebSecurityExpressionHandler * /
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler(a) {
return webSecurity.getExpressionHandler();
}
/ * * * Spring Security Filter core Spring Security Filter Chain, Bean ID for springSecurityFilterChain *@return the {@link Filter} that represents the security filter chain
* @throws Exception
*/
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain(a) throws Exception {
booleanhasConfigurers = webSecurityConfigurers ! =null
&& !webSecurityConfigurers.isEmpty();
if(! hasConfigurers) { WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor .postProcess(new WebSecurityConfigurerAdapter() {
});
webSecurity.apply(adapter);
}
return webSecurity.build();
}
/** ** Some page tag button support for templates such as JSP Freemarker * Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
* tag support.
* @return the {@link WebInvocationPrivilegeEvaluator}
* @throws Exception
*/
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public WebInvocationPrivilegeEvaluator privilegeEvaluator(a) throws Exception {
return webSecurity.getPrivilegeEvaluator();
}
/** ** To create the SecurityConfigurer instance of the Web Configuration. * Notice that this parameter passes@Value(...). Way of injection, the corresponding bean autowiredWebSecurityConfigurersIgnoreParents * * * defined in the class@param objectPostProcessor the {@link ObjectPostProcessor} used to create a
* {@link WebSecurity} instance
* @param webSecurityConfigurers the
* {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
* create the web configuration
* @throws Exception
*/
@Autowired(required = false)
public void setFilterChainProxySecurityConfigurer(
ObjectPostProcessor<Object> objectPostProcessor,
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
throws Exception {
webSecurity = objectPostProcessor
.postProcess(new WebSecurity(objectPostProcessor));
if(debugEnabled ! =null) {
webSecurity.debug(debugEnabled);
}
Collections.sort(webSecurityConfigurers, AnnotationAwareOrderComparator.INSTANCE);
Integer previousOrder = null;
Object previousConfig = null;
for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
if(previousOrder ! =null && previousOrder.equals(order)) {
throw new IllegalStateException(
"@Order on WebSecurityConfigurers must be unique. Order of "
+ order + " was already used on " + previousConfig + ", so it cannot be used on "
+ config + " too.");
}
previousOrder = order;
previousConfig = config;
}
for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
webSecurity.apply(webSecurityConfigurer);
}
this.webSecurityConfigurers = webSecurityConfigurers;
}
/** * Get all WebSecurityConfigurer beans from the current bean container. * these WebSecurityConfigurer is usually by the developers to realize the configuration of the class, and to inherit from WebSecurityConfigurerAdapter * * /
@Bean
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents( ConfigurableListableBeanFactory beanFactory) {
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
}
/**
* A custom verision of the Spring provided AnnotationAwareOrderComparator that uses
* {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
* instances for the {@link Order} annotation.
*
* @author Rob Winch
* @since3.2 * /
private static class AnnotationAwareOrderComparator extends OrderComparator {
private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();
@Override
protected int getOrder(Object obj) {
return lookupOrder(obj);
}
private static int lookupOrder(Object obj) {
if (obj instanceof Ordered) {
return ((Ordered) obj).getOrder();
}
if(obj ! =null) { Class<? > clazz = (objinstanceofClass ? (Class<? >) obj : obj.getClass()); Order order = AnnotationUtils.findAnnotation(clazz, Order.class);if(order ! =null) {
returnorder.value(); }}returnOrdered.LOWEST_PRECEDENCE; }}/* * To get the @enableWebSecurity attribute debugEnabled * * @see org.springframework.context.annotation.ImportAware#setImportMetadata(org. * springframework.core.type.AnnotationMetadata) */
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> enableWebSecurityAttrMap = importMetadata
.getAnnotationAttributes(EnableWebSecurity.class.getName());
AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes
.fromMap(enableWebSecurityAttrMap);
debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
if(webSecurity ! =null) { webSecurity.debug(debugEnabled); }}/* * (non-Javadoc) * * @see * org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java. * lang.ClassLoader) */
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader; }}Copy the code
3.2 SpringWebMvcImportSelector
This class is designed to support Spring Mvc. Once found application using the Spring Mvc core will introduce WebMvcSecurityConfiguration DispatcherServlet front controller. The main purpose is to adapt to Spring Mvc.
3.3 OAuth2ImportSelector
This class is intended to support OAuth2.0 open authorization protocols. ClientRegistration if referenced, specifically when the Spring-Security-OAuth2 module is enabled (introducing dependent JARS). Will enable OAuth2ClientConfiguration OAuth2 client configuration.
3.4 @ EnableGlobalAuthentication
This class is mainly introduced the AuthenticationManager AuthenticationConfiguration purpose mainly in order to structure the authentication manager. The AuthenticationManager is very important and we will do a special analysis later.
4. SecurityFilterAutoConfiguration
We in the org. Springframework. Boot. Autoconfigure. Security. The servlet path has found a configuration class SecurityFilterAutoConfiguration. This class is used to registered a name with the Servlet container for securityFilterChainRegistration bean, implementation class is DelegatingFilterProxyRegistrationBean. The purpose of this bean is to register another Servlet Filter bean into the Servlet container, implementing class DelegatingFilterProxy. DelegatingFilterProxy is actually a proxy Filter that, when used by the Servlet container to process a request, delegates the task to another Filter bean assigned to it. For SecurityFilterAutoConfiguration, By the agent of the Filter bean springSecurityFilterChain name, also is the Spring Security Web we mentioned above provide for the request of safe handling of the Filter bean, The implementation class is FilterChainProxy.
Related source code analysis:
package org.springframework.boot.autoconfigure.security.servlet;
import java.util.EnumSet;
import java.util.stream.Collectors;
import javax.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
@Configuration
// Only in Servlet environment
@ConditionalOnWebApplication(type = Type.SERVLET)
// Ensure that the security property configuration information is loaded and registered with the container as a bean
@EnableConfigurationProperties(SecurityProperties.class)
// Only if the specific class exists on the classpath
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class,
SessionCreationPolicy.class })
/ / specify the configuration classes in SecurityAutoConfiguration configuration application after application
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
// DelegatingFilterProxy Filter to register with the Servlet container
/ / target proxy Filter bean name: springSecurityFilterChain
private static final String DEFAULT_FILTER_NAME =
AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
/ / define a bean securityFilterChainRegistration,
// The purpose of this bean is to register another bean to the Servlet container: a Servlet Filter implementing class DelegatingFilterProxy
// The DelegatingFilterProxy Filter is actually a proxy Filter that is used by the Servlet container to match requests for a specific URL pattern,
/ / and it will entrust tasks assigned to his own name for springSecurityFilterChain Filter, which is Spring Security Web
// provides a Filter bean for request security processing, whose implementation class is FilterChainProxy
// (HttpFirewall + n SecurityFilterChain)
@Bean
@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;
}
returnsecurityProperties.getFilter().getDispatcherTypes().stream() .map((type) -> DispatcherType.valueOf(type.name())).collect(Collectors .collectingAndThen(Collectors.toSet(), EnumSet::copyOf)); }}Copy the code
5. To summarize
This article mainly provides a rough introduction to Spring Security’s automatic configuration mechanisms in Spring Boot. Why not go into detail. Because from the start of learning some things we do not have to in-depth understanding, but to know a little bit of relevant knowledge. Let’s just get a general idea. So don’t get bogged down in reading this article. A rough understanding of the role of configuration policy, load policy, and some key classes is enough. After you’ve learned more about Spring Security, a closer look at these configuration classes will give you more insight. On the other hand, this article also gives you some ideas for reading Spring source code, which is the most important thing to learn.
Follow our public id: Felordcn for more information
Personal blog: https://felord.cn