• spring boot 2.x静态资源会被HandlerInterceptor拦截的原因和解决方法


    在spring boot 1.5.x中,resources/static目录下的静态资源可以直接访问,并且访问路径上不用带static,比如静态资源放置位置如下图所示:

    静态资源目录结构

    那么访问静态资源的路径可以是:

    当有配置自定义HandlerInterceptor拦截器时,请求以上静态资源路径不会被拦截。自定义HandlerInterceptor拦截器源码如下:

    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;
    
    
    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);
        }
    }

    配置如下:

    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;
    
    
    @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());
        }
    
    }

    访问静态资源时路径上不用加static目录:

    spring boot 1.5.x访问静态资源

    当spring boot版本升级为2.x时,访问静态资源就会被HandlerInterceptor拦截

    HandlerInterceptor拦截静态资源日志

    这样对于利用HandlerInterceptor来处理访问权限或其他相关的功能就会受影响,跟踪源码查看原因,是因为spring boot 2.x依赖的spring 5.x版本,相对于spring boot 1.5.x依赖的spring 4.3.x版本而言,针对资源的拦截器初始化时有区别,具体源码在WebMvcConfigurationSupport中,spring 4.3.x源码如下:

    /**
     * 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;
    }

    而spring 5.x的源码如下:

    /**
     * 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();
    }

    从源码当中可以看出,使用spring 5.x时,静态资源也会执行自定义的拦截器,因此在配置拦截器的时候需要指定排除静态资源的访问路径,即配置改为如下即可:

    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/**"));
        }
    }

    这样就可以和spring boot 1.5.x一样的方式使用了。不过从源码当中可以看出,每个静态资源的请求都会被自定义Interceptor拦截,只是通过访问路径判断后不会执行拦截器的内容,所以spring 5.x相对于spring 4.3.x而言,这部分处理的性能会更低一些

    说明:

    • 本文中测试使用的具体版本:

      • spring-boot-1.5.3.RELEASE(相对应的spring版本是spring-webmvc-4.3.8.RELEASE)

      • spring-boot-2.0.1.RELEASE(相对应的spring版本是spring-webmvc-5.0.5.RELEASE)

    • 关于配置类,在spring boot 2.x已经改为最低支持jdk8版本,而jdk8中的接口允许有默认实现,所以已经废弃掉WebMvcConfigurerAdapter适配类,而改为直接实现WebMvcConfigurer接口

     
  • 相关阅读:
    我的博客开通了啦!
    今天终于下定决心,辞掉现在的工作,开始导找新的机会。
    C# 文件流的使用
    XNA学习笔记(二) 发布release版本出现的问题
    Unity3D学习笔记(三) 数组和容器(泛型)使用学习(基于C#)
    Unity3D学习笔记(六) 关于碰撞
    Unity3D学习笔记(一) 模型和贴图导入学习
    Unity3D学习笔记(八) 保存数据的简单方式
    Unity3D学习笔记(五) C#基础学习
    Unity3D学习笔记(四) 脚本内访问其他对象,组件或脚本成员方法总结
  • 原文地址:https://www.cnblogs.com/tianlong/p/10577379.html
Copyright © 2020-2023  润新知