spring-security (sixteen) Filter configuration principle

Foreword:
  The most common application scenarios of spring security are web applications based on http requests and servlet APIs. In the next few chapters, we will focus on how spring security implements authentication and access control in web applications. From the previous chapters, we have learned that in web applications, spring uses various filters for authentication and authentication. This section mainly discusses several main components of filters in spring.
1.DelegatingFilterProxy
In order to make the Filter work, we must explicitly declare it in web.xml, or add it to the servlet through the ServletContext.addFilter method added in servlet3.0+, otherwise the filter will be ignored by the servlet container. In spring security, our filter is also a spring bean, and we also need to use the dependency injection function to assemble, and we know that under normal circumstances, the creation of the servlet context is before the application context. If we directly register our filter to In the servlet, the object injected by the dependency in the filter will not be obtained. In order to solve this problem, spring introduces the DelegatingFilterProxy class, which provides a link between the servlet context and the application context.
DelegatingFilterProxy is used in web.xml, usually using the following configuration snippet
<filter>
 <filter-name>myFilter</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>myFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>


The corresponding java config snippet is as follows:
Filter filter = new DelegatingFilterProxy("myFilter");
servletContext.addFilter("myFilter", filter)
                        .addMappingForUrlPatterns (
                         EnumSet.of(DispatcherType.FORWARD,
                                              DispatcherType.INCLUDE,
                                              DispatcherType.REQUEST), false, "/*");

It can be seen that the Filter we actually configured is DelegatingFilterProxy, not the class we actually implemented the Filter interface. When there is a request that satisfies the UriPatterns of DelegatingFilterProxy, the DelegatingFilterProxy class will first determine whether the actual Filter class has been configured. Get the corresponding bean from the spring context based on the configured bean name, and then delegate the processing to our real Filter class. In this way, our real Filter class can use spring bean life cycle methods and flexible dependency injection configuration. It should be noted that the bean name injected into the spring context by our Filter class must be the same as the name passed into the DelegatingFilterProxy. The code snippet for processing the request in DelegatingFilterProxy is as follows:
@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		// Lazily initialize the delegate if necessary.
		Filter delegateToUse = this.delegate;
		if (delegateToUse == null) {
			synchronized (this.delegateMonitor) {
				if (this.delegate == null) {
				WebApplicationContext wac = findWebApplicationContext();
				if (wac == null) {
				throw new IllegalStateException("No WebApplicationContext found: " +
					"no ContextLoaderListener or DispatcherServlet registered?");
					}
					this.delegate = initDelegate(wac);
				}
				delegateToUse = this.delegate;
			}
		}

		// Let the delegate perform the actual doFilter operation.
		invokeDelegate(delegateToUse, request, response, filterChain);
	}

......

protected void invokeDelegate(
			Filter delegate, ServletRequest request, ServletResponse response,FilterChain filterChain)
			throws ServletException, IOException {

		delegate.doFilter(request, response, filterChain);
	}


2. FilterChainProxy
In spring security, we register in the servlet context through DelegatingFilterProxy, not a single filter, but a FilterChainProxy. A FilterChainProxy is a proxy for a series of filter chains, which contains many filter chains (the filter chain is in The unified interface in spring is SecurityFilterChain, each SecurityFilterChain contains a sorted list of filters), when FilterChainProxy receives a request entrusted by DelegatingFilterProxy, it finds the first one that satisfies the current request from these filter chains , and obtain the filter list in this filter chain, and then execute the obtained filter list in turn. Although theoretically we can configure the filters we need to web.xml one by one through DelegatingFilterProxy or append them to the servlet through the servletContext.addFilter method, this method will become difficult to maintain as we add filters, and in this way As a result, the execution order of filters will strictly depend on the order in which we append, which lacks flexibility. Below is an example
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy"> <constructor-arg>
<list>
<sec:filter-chain pattern="/restful/**" filters="
  securityContextPersistenceFilterWithASCFalse,
  basicAuthenticationFilter,
  exceptionTranslationFilter,
  filterSecurityInterceptor" />
<sec:filter-chain pattern="/**" filters="
 securityContextPersistenceFilterWithASCTrue,
 formLoginFilter,
 exceptionTranslationFilter,
 filterSecurityInterceptor" />
 </list>
</constructor-arg>
</bean>



Among the multiple filter chains in FilterChainProxy, we use the first filter chain that matches the current request to filter the request, so the more specific URI should be configured, the higher it should be.


3. Let's take a look at the spring How to register the filter we need into the servlet in security
3.1 The first is the performBuild method of the WebSecurity class,
this method will append us to ignoredRequests (configured through the ignoring() method) and the configured securityFilterChainBuilders (actually HttpSecurity) respectively Configured as SecurityFilterChain, the code snippet is as follows:
@Override
	protected Filter performBuild() throws Exception {
		...
		int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
		List<SecurityFilterChain> securityFilterChains = new ArrayList<SecurityFilterChain>(
				chainSize);
		for (RequestMatcher ignoredRequest : ignoredRequests) {
			securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
		}
		for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
			securityFilterChains.add(securityFilterChainBuilder.build());
		}
		FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
...

3.2 After the configured FilterChainProxy is returned, it will be registered to the spring context in WebSecurityConfiguration, and the bean name is springSecurityFilterChain
/**
	 * Creates the Spring Security Filter Chain
	 * @return
	 * @throws Exception
	 */
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}

If we have a configuration class that inherits WebSecurityConfigurerAdapter, we will use our own.
The specific value of AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME is springSecurityFilterChain
public abstract class AbstractSecurityWebApplicationInitializer
		implements WebApplicationInitializer {

...
public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
...

3.3 Register the bean named springSecurityFilterChain into the servlet through DelegatingFilterProxy. In the spring boot project, this is achieved through the automatic registration class SecurityFilterAutoConfiguration
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;

	@Bean
	@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
	public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
			SecurityProperties securityProperties) {
		DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
				DEFAULT_FILTER_NAME);
		registration.setOrder(securityProperties.getFilterOrder());
		registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
		return registration;
	}

It can be seen that when a bean named springSecurityFilterChain exists in our context, spring boot will automatically register a DelegatingFilterProxyRegistrationBean for us and pass springSecurityFilterChain in. The inheritance relationship of the bean of DelegatingFilterProxyRegistrationBean is
DelegatingFilterProxyRegistrationBean ->AbstractFilterRegistrationBean->RegistrationBean->ServletContextInitializer , so the onStartup method will be launched when the servlet starts. Inside this method, the specific implementation is in the AbstractFilterRegistrationBean class.
@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		Filter filter = getFilter();
		Assert.notNull(filter, "Filter must not be null");
		String name = getOrDeduceName(filter);
		if (!isEnabled()) {
			this.logger.info("Filter " + name + " was not registered (disabled)");
			return;
		}
		FilterRegistration.Dynamic added = servletContext.addFilter(name, filter);
		if (added == null) {
			this.logger.info("Filter " + name + " was not registered "
					+ "(possibly already registered?)");
			return;
		}
		configure(added);
	}

As you can see, a filter is obtained here and registered in the servletContext. The specific getFilter method is in DelegatingFilterProxyRegistrationBean
@Override
	public Filter getFilter() {
		return new DelegatingFilterProxy(this.targetBeanName,
				getWebApplicationContext()) {

			@Override
			protected void initFilterBean() throws ServletException {
				// Don't initialize filter bean on init()
			}

		};
	}

Obviously, a DelegatingFilterProxy is used to wrap the bean name we pass in. So far, the filter used in spring security has been registered to the servlet in the form of DelegatingFilterProxy.

Specifically, how the filters we want to use, such as UsernamePasswordAuthenticationFilter, SecurityContextPersistenceFilter, etc., are added to HttpSecurity, and then added to SecurityFilterChain in step 3.1. We will continue to explain when we discuss each filter in detail

. 4. Bypass filter
in practical application development , for access to some resources, we may not want to use spring security for protection, such as some static js, html, etc. The following is an example of implementing this function in two configuration forms, xml and java config. In the
xml configuration, we You can set the filter of the corresponding URI to none, as follows
<http pattern="/static/**" security="none"/>
<http pattern="/resources/**" security="none"/>

Corresponding java config
@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/resources/**", "/static/**");
	}

Through section 3.1, we know that the filtering rules configured through web.ignoring() will be configured as DefaultSecurityFilterChain to be appended to the top of the list of filter chains, and only RequestMatcher is passed in when DefaultSecurityFilterChain is configured, and no filter is passed in. The DefaultSecurityFilterChain constructor is as follows,
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
		this(requestMatcher, Arrays.asList(filters));
	}

Indicates that no filter is applied to the uri that meets the conditions, which is the same as the effect of security="none" in xml.
Another point to note is that with this configuration, any URI that meets the conditions will not be processed by the spring security filter, and the SecurityContext will not contain any user information.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326158379&siteId=291194637