版本控制静态资产不适用于 Spring Web Flow

Versioning static assets does not work with Spring Web Flow

资源文件的缓存过期和版本控制在所有页面上都能正常工作。但是流程似乎忽略了 Spring MVC 配置。

一个工作示例:

资源文件有版本控制

使用 Spring 网络流量:

资源文件缺少版本控制

在 WebMvcConfig class:

@Configuration
@EnableCaching
@ConfigurationProperties("message")
public class WebMvcConfig implements WebMvcConfigurer, ServletContextAware {

    ...

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/resources/")
                .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                .resourceChain(false)
                .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                .addTransformer(new CssLinkResourceTransformer());
    }

    @Bean
    public ResourceUrlEncodingFilter resourceUrlEncodingFilter() {
        return new ResourceUrlEncodingFilter();
    }

    ...
}

WebConfigClass:

@Configuration
@EnableWebMvc
public class WebFlowConfig extends AbstractFlowConfiguration {
    @Autowired
    private ViewResolver viewResolver;
    @Autowired
    private RequestDataInterceptor requestDataInterceptor;
    @Autowired
    private LocalValidatorFactoryBean validator;

    // WEB FLOW
    @Bean
    public FlowExecutor flowExecutor() {
        return getFlowExecutorBuilder(flowRegistry()).addFlowExecutionListener(new SecurityFlowExecutionListener(), "*").build();
    }

    @Bean
    public FlowDefinitionRegistry flowRegistry() {
        return getFlowDefinitionRegistryBuilder(flowBuilderServices()).setBasePath("/WEB-INF/flows/").addFlowLocationPattern("/**/*-flow.xml").build();
    }

    @Bean
    public FlowBuilderServices flowBuilderServices() {
        return getFlowBuilderServicesBuilder().setViewFactoryCreator(mvcViewFactoryCreator()).setValidator(validator).setDevelopmentMode(true).setConversionService(getDefaultConversionService()).build();
    }

    private DefaultConversionService getDefaultConversionService() {
        final DefaultConversionService service = new DefaultConversionService();
        final FormattingConversionService delegateConversionService = (FormattingConversionService) service.getDelegateConversionService();
        delegateConversionService.removeConvertible(String.class, Number.class);
        delegateConversionService.addConverterFactory(new StringToNumberConverterFactory());
        delegateConversionService.addConverter(new TrimStringConverter());
        return service;
    }

    // MVC
    @Bean
    public FlowHandlerMapping flowHandlerMapping() {
        final FlowHandlerMapping mapping = new FlowHandlerMapping();
        mapping.setOrder(0);
        mapping.setFlowRegistry(this.flowRegistry());
        mapping.setInterceptors(requestDataInterceptor);
        return mapping;
    }

    @Bean
    public FlowHandlerAdapter flowHandlerAdapter() {
        final FlowHandlerAdapter adapter = new FlowHandlerAdapter();
        adapter.setFlowExecutor(this.flowExecutor());
        adapter.setSaveOutputToFlashScopeOnRedirect(true);
        return adapter;
    }

    @Bean
    public MvcViewFactoryCreator mvcViewFactoryCreator() {
        final MvcViewFactoryCreator factoryCreator = new MvcViewFactoryCreator();
        factoryCreator.setViewResolvers(Lists.newArrayList(this.viewResolver));
        factoryCreator.setUseSpringBeanBinding(true);
        return factoryCreator;
    }
}

在安全配置中:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    ... 

    @Override
    public void configure(final WebSecurity web) {
        web.ignoring().antMatchers("/resources/**");
    }

    ...
}

在jsp个文件中(包括流程):

<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%>
...
<!-- jQuery -->
<script type="text/javascript" src="<c:url value="/resources/scripts/libs/jquery-3.3.1.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/scripts/jquery-validation/jquery.validate.js"/>"></script>
<script type="text/javascript" src="<c:url value="/resources/scripts/jquery-numbers/jquery.number.min.js"/>"></script>
...

关于如何在流上应用静态资产的版本控制有什么想法吗?

我的处理方式:

<spring:eval expression="@applicationProperties.resourcesVersion" var="resourcesVersion"/>
<head>
    <style type="text/css" media='screen,print'>
        @import url("<c:url value="/resources/css-framework/css/tools.css?v=${resourcesVersion}" />");
    </style>
    <script type="text/javascript" src="<c:url value="/resources/spring/Spring.js?v=${resourcesVersion}" />"></script>
</head>

应用程序属性:

@Named
public class ApplicationProperties {

    ....

    private Long resourcesVersion = System.currentTimeMillis();

    public Long getResourcesVersion() {
        return resourcesVersion;
    }
}

我在 CAS 服务器中遇到了同样的问题,解决方案是添加 ResourceUrlProviderExposingInterceptor 作为 FlowHandlerMapping 的拦截器。

参见:https://github.com/apereo/cas/pull/5266/files#diff-5c92401d9bb5992a2273618310a30ff672ff45e00d691eb651e6187ceedb0869R197