diff --git a/src/main/java/fr/recia/glc/configuration/SecurityConfiguration.java b/src/main/java/fr/recia/glc/configuration/SecurityConfiguration.java index db0f380a..794ca315 100644 --- a/src/main/java/fr/recia/glc/configuration/SecurityConfiguration.java +++ b/src/main/java/fr/recia/glc/configuration/SecurityConfiguration.java @@ -207,7 +207,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authz -> authz .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .antMatchers("/app/**/*.{js,html}").permitAll() + .antMatchers("/app/**/*.{js,html}", "/ui/**").permitAll() .antMatchers("/health-check", "/api/config").permitAll() .antMatchers("/api/**").hasAuthority(AuthoritiesConstants.USER) .antMatchers( diff --git a/src/main/java/fr/recia/glc/configuration/WebConfigurer.java b/src/main/java/fr/recia/glc/configuration/WebConfigurer.java new file mode 100644 index 00000000..003ee3a1 --- /dev/null +++ b/src/main/java/fr/recia/glc/configuration/WebConfigurer.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2023 GIP-RECIA, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fr.recia.glc.configuration; + +import fr.recia.glc.web.filter.StaticResourcesProductionFilter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.server.MimeMappings; +import org.springframework.boot.web.server.WebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import org.springframework.http.MediaType; + +import javax.inject.Inject; +import javax.servlet.DispatcherType; +import javax.servlet.FilterRegistration; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.EnumSet; + +; + +/** + * Configuration of web application with Servlet 3.0 APIs. + */ +@Slf4j +@Configuration +public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer { + + @Inject + private Environment env; + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + log.info("Web application configuration, using profiles: {}", Arrays.toString(env.getActiveProfiles())); + EnumSet disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC); + if (env.acceptsProfiles(Profiles.of(Constants.SPRING_PROFILE_PRODUCTION))) { +// initCachingHttpHeadersFilter(servletContext, disps); + initStaticResourcesProductionFilter(servletContext, disps); +// initGzipFilter(servletContext, disps); + } + log.info("Web application fully configured"); + } + + /** + * Set up Mime types. + */ + @Override + public void customize(WebServerFactory server) { + if (server instanceof ConfigurableServletWebServerFactory) { + MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT); + // IE issue, see https://github.com/jhipster/generator-jhipster/pull/711 + mappings.add("html", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase()); + // CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64 + mappings.add("json", MediaType.TEXT_HTML_VALUE + ";charset=" + StandardCharsets.UTF_8.name().toLowerCase()); + ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server; + servletWebServer.setMimeMappings(mappings); + } + } + +// /** +// * Initializes the GZip filter. +// */ +// private void initGzipFilter(ServletContext servletContext, EnumSet disps) { +// log.debug("Registering GZip Filter"); +// FilterRegistration.Dynamic compressingFilter = servletContext.addFilter("gzipFilter", new GZipServletFilter()); +// Map parameters = new HashMap<>(); +// compressingFilter.setInitParameters(parameters); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.css"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.json"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.html"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.js"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.svg"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.ttf"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "*.woff2"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "/api/*"); +// compressingFilter.addMappingForUrlPatterns(disps, true, "/management/*"); +// compressingFilter.setAsyncSupported(true); +// } + + /** + * Initializes the static resources production Filter. + */ + private void initStaticResourcesProductionFilter(ServletContext servletContext, EnumSet disps) { + + log.debug("Registering static resources production Filter"); + FilterRegistration.Dynamic staticResourcesProductionFilter = servletContext.addFilter("staticResourcesProductionFilter", new StaticResourcesProductionFilter()); + + staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/"); + staticResourcesProductionFilter.addMappingForUrlPatterns(disps, true, "/ui/*"); + staticResourcesProductionFilter.setAsyncSupported(true); + } + +// /** +// * Initializes the cachig HTTP Headers Filter. +// */ +// private void initCachingHttpHeadersFilter(ServletContext servletContext, EnumSet disps) { +// log.debug("Registering Caching HTTP Headers Filter"); +// FilterRegistration.Dynamic cachingHttpHeadersFilter = servletContext.addFilter("cachingHttpHeadersFilter", new CachingHttpHeadersFilter()); +// +// cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/ui/assets/*"); +// cachingHttpHeadersFilter.setAsyncSupported(true); +// } + +} diff --git a/src/main/java/fr/recia/glc/web/filter/StaticResourcesProductionFilter.java b/src/main/java/fr/recia/glc/web/filter/StaticResourcesProductionFilter.java new file mode 100644 index 00000000..5e123200 --- /dev/null +++ b/src/main/java/fr/recia/glc/web/filter/StaticResourcesProductionFilter.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 GIP-RECIA, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fr.recia.glc.web.filter; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.regex.Pattern; + +/** + * This filter is used in production, to serve static resources generated by "grunt build". + *

+ *

+ * It is configured to serve resources from the "dist" directory, which is the Grunt + * destination directory. + *

+ */ +@Slf4j +public class StaticResourcesProductionFilter implements Filter { + + /** + * Pattern pour les ressources statiques + */ + private Pattern staticResourcesPattern = Pattern.compile("^/ui/(assets/.*)$"); + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + String contextPath = httpRequest.getContextPath(); + String requestURI = httpRequest.getRequestURI(); + requestURI = StringUtils.substringAfter(requestURI, contextPath); + if (StringUtils.equals("/", requestURI)) { + // Redirection permanaente vers /ui si on essaye d'accéder à / + log.debug("Permanent redirection from / to /ui/"); + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); + httpResponse.setHeader("Location", contextPath + "/ui/"); + } else { + if (StringUtils.equals("/ui/", requestURI) || !staticResourcesPattern.matcher(requestURI).matches()) { + requestURI = "/ui/index.html"; + } + String newURI = requestURI.replace("/ui/", "/dist/"); + log.debug("RequestDispatcher - setting newURI to {}", newURI); + request.getRequestDispatcher(newURI).forward(request, response); + } + } + +}