Reasons and solutions why spring boot 2.x static resources are intercepted by HandlerInterceptor

In spring boot 1.5.x, the static resources in the resources/static directory can be accessed directly, and the access path does not need static. For example, the static resources are placed as shown in the following figure:

Static resource directory structure

Then the path to access static resources can be:

When a custom HandlerInterceptor is configured, requests for the above static resource paths will not be intercepted. The source code of the custom HandlerInterceptor interceptor is as follows:

package com.itopener.demo.config;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * @author fuwei.deng
 * @date 2018年4月13日 下午3:32:26
 * @version 1.0.0
 */
public class LoginRequiredInterceptor extends HandlerInterceptorAdapter {

	private final Logger logger = LoggerFactory.getLogger(LoginRequiredInterceptor.class);

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		logger.info(request.getRequestURI());
		return super.preHandle(request, response, handler);
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		logger.info(request.getRequestURI());
		super.afterCompletion(request, response, handler, ex);
	}
}

The configuration is as follows:

package com.itopener.demo.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

/**
 * @author fuwei.deng
 * @date 2018年4月13日 下午3:32:54
 * @version 1.0.0
 */
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {

	private final Logger logger = LoggerFactory.getLogger(WebMvcConfiguration.class);

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		logger.info("add interceptors");
		registry.addInterceptor(new LoginRequiredInterceptor());
	}

}

When accessing static resources, there is no need to add the static directory to the path:

spring boot 1.5.x access static resources

When the spring boot version is upgraded to 2.x, access to static resources will be intercepted by HandlerInterceptor

HandlerInterceptor intercepts static resource logs

In this way, the use of HandlerInterceptor to process access rights or other related functions will be affected. The reason for tracking the source code is because the spring 5.x version that spring boot 2.x depends on is relative to the spring 4.3 that spring boot 1.5.x depends on. For the x version, there are differences in the initialization of the interceptor for resources. The specific source code is in WebMvcConfigurationSupport. The source code of spring 4.3.x is as follows:

/**
 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
 * resource handlers. To configure resource handling, override
 * {@link #addResourceHandlers}.
 */
@Bean
public HandlerMapping resourceHandlerMapping() {
    ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
				this.servletContext, mvcContentNegotiationManager());
    addResourceHandlers(registry);

    AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
    if (handlerMapping != null) {
        handlerMapping.setPathMatcher(mvcPathMatcher());
        handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
        // 此处固定添加了一个Interceptor
        handlerMapping.setInterceptors(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
        handlerMapping.setCorsConfigurations(getCorsConfigurations());
		}
    else {
        handlerMapping = new EmptyHandlerMapping();
    }
    return handlerMapping;
}

The source code of spring 5.x is as follows:

/**
 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
 * resource handlers. To configure resource handling, override
 * {@link #addResourceHandlers}.
 */
@Bean
public HandlerMapping resourceHandlerMapping() {
    Assert.state(this.applicationContext != null, "No ApplicationContext set");
    Assert.state(this.servletContext != null, "No ServletContext set");

    ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
				this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
    addResourceHandlers(registry);

    AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
    if (handlerMapping != null) {
        handlerMapping.setPathMatcher(mvcPathMatcher());
        handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
        // 此处是将所有的HandlerInterceptor都添加了(包含自定义的HandlerInterceptor)
        handlerMapping.setInterceptors(getInterceptors());
        handlerMapping.setCorsConfigurations(getCorsConfigurations());
    }
    else {
        handlerMapping = new EmptyHandlerMapping();
    }
    return handlerMapping;
}

/**
 * Provide access to the shared handler interceptors used to configure
 * {@link HandlerMapping} instances with. This method cannot be overridden,
 * use {@link #addInterceptors(InterceptorRegistry)} instead.
 */
protected final Object[] getInterceptors() {
    if (this.interceptors == null) {
        InterceptorRegistry registry = new InterceptorRegistry();
        // 此处传入新new的registry对象,在配置类当中设置自定义的HandlerInterceptor后即可获取到
        addInterceptors(registry);
        registry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService()));
        registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider()));
        this.interceptors = registry.getInterceptors();
    }
    return this.interceptors.toArray();
}

As can be seen from the source code, when using spring 5.x, static resources will also execute custom interceptors, so when configuring the interceptor, you need to specify the access path that excludes static resources, that is, the configuration can be changed to the following:

package com.itopener.demo.config;

import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author fuwei.deng
 * @date 2018年4月13日 下午3:32:54
 * @version 1.0.0
 */
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

	private final Logger logger = LoggerFactory.getLogger(WebMvcConfiguration.class);

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		logger.info("add interceptors");
		registry.addInterceptor(new LoginRequiredInterceptor()).excludePathPatterns(Arrays.asList("/views/**", "/res/**"));
	}
}

This can be used in the same way as spring boot 1.5.x. However, it can be seen from the source code that each static resource request will be intercepted by the custom Interceptor, but the content of the interceptor will not be executed after the access path judgment, so compared with spring 4.3.x, spring 5.x, this Some processing performance will be lower

illustrate:

  • The specific version tested in this article:

    • spring-boot-1.5.3.RELEASE (the corresponding spring version is spring-webmvc-4.3.8.RELEASE)

    • spring-boot-2.0.1.RELEASE (the corresponding spring version is spring-webmvc-5.0.5.RELEASE)

  • Regarding the configuration class, spring boot 2.x has been changed to support the minimum jdk8 version, and the interface in jdk8 allows default implementation, so the WebMvcConfigurerAdapter adaptation class has been abandoned, and the WebMvcConfigurer interface is directly implemented instead.

Guess you like

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