spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.22k stars 40.7k forks source link

2.1.3.RELEASE to 2.6.5 static resources cannot be accessed #30449

Closed ZZQ001010 closed 2 years ago

ZZQ001010 commented 2 years ago

Hello!, I upgraded the springboot version from 2.1.3.RELEASE to 2.6.5, and I found that my static resources could not be accessed.

Here are some of my configs

package cn.sunline.web.config;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import javax.annotation.Resource;

import org.apache.catalina.Context;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.security.access.vote.AuthenticatedVoter;
import org.springframework.security.access.vote.RoleVoter;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

import cn.sunline.common.conf.KiteInitAutoConfiguration;
import cn.sunline.web.common.listener.CommonSessionAndRequestListener;
import cn.sunline.web.common.mvc.handler.KiteWebExceptionResolver;
import cn.sunline.web.common.security.AccessDecisionManager;
import cn.sunline.web.common.security.AuthenticationFailure;
import cn.sunline.web.common.security.AuthenticationSuccess;
import cn.sunline.web.common.security.KiteSecurityPasswordEncoder;
import cn.sunline.web.common.security.LogoutSuccessHandler;
import cn.sunline.web.common.security.SecurityFacility;
import cn.sunline.web.common.security.SecurityServiceImpl;
import cn.sunline.web.common.security.UnauthorizedEntryPoint;
import cn.sunline.web.common.security.UrlAccessDecisionVoter;
import cn.sunline.web.common.security.filter.OrganizationContextFilter;
import cn.sunline.web.common.security.filter.XssFilter;

/**
 * @author fengliming
 *
 */
@Configuration
@AutoConfigureAfter(KiteInitAutoConfiguration.class)
@EnableWebMvc
@ComponentScan(basePackages = { "cn.sunline" }, includeFilters = {
        @Filter(type = FilterType.ANNOTATION, value = Controller.class) })
@SuppressWarnings("all")
public class KiteWebAutoConfiguration {

    /**
     * 静态资源图片不经过DispacherServlet
     */
    @Configuration
    @EnableWebMvc
    public class KiteWebMvcConfigurer implements WebMvcConfigurer {

        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("forward:/home.in");
            registry.addStatusController("/WEB-INF/pages/error/500.jsp", HttpStatus.INTERNAL_SERVER_ERROR);
            registry.addStatusController("/WEB-INF/pages/error/404.jsp", HttpStatus.NOT_FOUND);
            registry.addStatusController("/WEB-INF/pages/error/403.jsp", HttpStatus.FORBIDDEN);
            WebMvcConfigurer.super.addViewControllers(registry);
        }

        @Override
        public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
            configurer.useRegisteredExtensionsOnly(false).favorPathExtension(false).favorParameter(true).parameterName("mediaType")
                .ignoreAcceptHeader(true).defaultContentType(MediaType.APPLICATION_JSON);
        }

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/static/**","/**/*.js","/**/*.png","/**/*.jpg","/**/*.ico","/**/*.gif","/**/).css","/**/*.htm",
                    "/**/*.html","/static/**","/login.in","/setLanguage.in","/sessionServer/timeout.do", "/user/getLogoImgName.in");
            registry.hasMappingForPattern("*.in");
        }

//      public void addViewControllers(ViewControllerRegistry registry) {
//             registry.addRedirectViewController("/", "/static/kindeditor/license.txt");
//      }

        /** 
         * <p>
         * spring mvc统一异常处理类
         * </p>
         * @return
         */
        @Bean
        @Order(0)
        public HandlerExceptionResolver handlerExceptionResolver() {
            return new KiteWebExceptionResolver();
        }

        @Bean
        public LocaleChangeInterceptor localeChangeInterceptor() {
            LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
            lci.setParamName("lang");
            return lci;
        }

        @Bean
        public LocaleResolver localeResolver() {
            CookieLocaleResolver resolver = new CookieLocaleResolver();
            resolver.setCookieMaxAge(604800);
            resolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
            return resolver;
        }

        /**
         * 文件上传,默认最大允许10M
         * 
         * @param maxUploadSize
         * @return
         */
        @Bean
        CommonsMultipartResolver commonsMultipartResolver(
                @Value("#{env['maxUploadSize']?:10485760}") long maxUploadSize) {
            CommonsMultipartResolver resolver = new CommonsMultipartResolver();
            resolver.setMaxUploadSize(maxUploadSize);
            return resolver;
        }
    }

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addContextCustomizers((context)->context.addWelcomeFile("home.in"));
        return factory;
    }

    @Bean
    public ServletListenerRegistrationBean commonSessionAndRequestListener(){
        ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
        servletListenerRegistrationBean.setListener(new CommonSessionAndRequestListener());
        return servletListenerRegistrationBean;
    }

//  @Bean
//  public RequestMappingHandlerMapping buildRequestMappingHandlerMapping() {
//      return new RequestMappingHandlerMapping();
//  }

    @Bean
    public AbstractHandlerMethodAdapter buildRequestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter handler = new RequestMappingHandlerAdapter();
        handler.setOrder(10000);
        handler.setWebBindingInitializer(buildConfigurableWebBindingInitializer());
        handler.setMessageConverters(Arrays.asList(buildMappingJackson2HttpMessageConverter()));
        return handler;
    }

    @Resource(name="conversionService")
    private GenericConversionService conversionService;

    @Bean
    public ConfigurableWebBindingInitializer buildConfigurableWebBindingInitializer() {
        ConfigurableWebBindingInitializer config = new ConfigurableWebBindingInitializer();
        config.setConversionService(conversionService);
        return config;
    }

    @Bean
    public MappingJackson2HttpMessageConverter buildMappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.APPLICATION_JSON_UTF8);
//      list.add("text/json;charset=utf-8");
//      list.add("application/json;charset=utf-8");
        converter.setSupportedMediaTypes(list);
        return converter;
    }

    @Bean
    public InternalResourceViewResolver buildInternalResourceViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class);
        return resolver;
    }

    @Bean
    public ServletRegistrationBean dispatcherRegistration(DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean reg = new ServletRegistrationBean(dispatcherServlet);
        reg.getUrlMappings().clear();
        reg.addUrlMappings("*.in");
        reg.addUrlMappings("/hello");
        reg.setLoadOnStartup(1);
        return reg;
    }

    @Bean
    public AuthenticationSuccess authenticationSuccess() {
        AuthenticationSuccess authenticationSuccess = new AuthenticationSuccess();
        authenticationSuccess.setTargetUrl("/home.in");
        return authenticationSuccess;
    }

    @Bean
    public AuthenticationFailure authenticationFailure() {
        AuthenticationFailure authenticationFailure = new AuthenticationFailure();
        authenticationFailure.setDefaultFailureUrl("/login.in?error=true");
        return authenticationFailure;
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler() {
        LogoutSuccessHandler logoutSuccessHandler = new LogoutSuccessHandler("/");
        return logoutSuccessHandler;
    }

    @Bean
    public OrganizationContextFilter organizationContextFilter() {
        return new OrganizationContextFilter();
    }

    @Bean
    public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
        FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<XssFilter>(new XssFilter());
        registration.setName("xssFilter");
        registration.addUrlPatterns("*.in");
        // 执行顺序,越小越优先
        registration.setOrder(0);
        return registration;
    }

    @Bean
    public SecurityServiceImpl securityServiceImpl() {
        SecurityServiceImpl securityServiceImpl = new SecurityServiceImpl();
        return securityServiceImpl;
    }

    @Bean
    public SecurityFacility securityFacility() {
        SecurityFacility securityFacility = new SecurityFacility();
        securityFacility.setSecurityService(securityServiceImpl());
        return securityFacility;
    }

    @Bean
    public KiteSecurityPasswordEncoder kiteSecurityPasswordEncoder() {
        KiteSecurityPasswordEncoder kiteSecurityPasswordEncoder = new KiteSecurityPasswordEncoder();
        return kiteSecurityPasswordEncoder;
    }

    @Bean
    public AccessDecisionManager accessDecisionManager() {
        @SuppressWarnings("unchecked")
        AccessDecisionManager accessDecisionManager = new AccessDecisionManager(
                Arrays.asList(new RoleVoter(), new AuthenticatedVoter(), new UrlAccessDecisionVoter()));
        return accessDecisionManager;
    }

    @Bean
    public UnauthorizedEntryPoint unauthorizedEntryPoint() {
        UnauthorizedEntryPoint unauthorizedEntryPoint = new UnauthorizedEntryPoint();
        unauthorizedEntryPoint.setLoginUrl("/login.in");
        return unauthorizedEntryPoint;
    }

    @Bean
    public ProviderManager authenticationManager() {
        ProviderManager authenticationManager = new ProviderManager(Arrays.asList(daoAuthenticationProvider()));
        return authenticationManager;
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(securityFacility());
        daoAuthenticationProvider.setPasswordEncoder(kiteSecurityPasswordEncoder());
        return daoAuthenticationProvider;
    }
}

2.1.3.RELEASE

image

2.6.5

image

I don't know why, since the request doesn't go into springmvc I can't debug Thanks!

wilkinsona commented 2 years ago

You are using @EnableWebMvc which completely disables Boot’s auto-configuration. You are also defining a registration for the dispatcher servlet and static resource mappings yourself. Given that you’re configuring so much yourself, it’s hard to see where Spring Boot could be causing the problem.

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

pruidong commented 2 years ago

You can refer to https://docs.spring.io/spring-boot/docs/2.6.5/reference/htmlsingle/#application-properties.web.spring.web.resources.static-locations to configure static resources. Use @ControllerAdvice to handle global exceptions.

hoowhoami commented 2 years ago

Modifying the configuration "spring.mvc.pathmatch.matching-strategy=ant_path_matcher" can solve this problem.

spring-projects-issues commented 2 years ago

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Toldwin commented 2 years ago

Hi, not sure it's 100% linked with your case but it's close enough to give you the information.

I had an issue upgrading 2.5.12 to 2.6.6 with static exposition of files because the key changed. I fixed it by changing the properties key :

Looks like spring.resources.static-locations has been deprecated. It's indeed specified with this key in the document pruidong has linked : https://docs.spring.io/spring-boot/docs/2.6.5/reference/htmlsingle/#application-properties.web.spring.web.resources.static-locations

Best regards,

wilkinsona commented 2 years ago

@hoowhoami @Toldwin Thank you for trying to help, but the use of @EnableWebMvc means that none of Spring Boot's MVC auto-configuration properties will take effect.

It has been just over two weeks since the original request for feedback so I am going to close this now. @zzq1314zll if you have a chance to prepare and share the requested sample we can take another look.