From dd827731b35f049d3b1830581e764ef76f323bb6 Mon Sep 17 00:00:00 2001 From: liaozan <378024053@qq.com> Date: Mon, 8 May 2023 22:13:48 +0800 Subject: [PATCH] Move WebCommonAutoConfiguration configurations --- .../web/AuthenticationConfiguration.java | 24 +++++++++ .../web/ExceptionHandingConfiguration.java | 30 +++++++++++ .../web/ServletComponentConfiguration.java | 39 ++++++++++++++ .../web/WebCommonAutoConfiguration.java | 52 ++++--------------- .../BodyParamMethodArgumentResolver.java | 29 +++++------ .../common/web/log/RequestLoggingFilter.java | 15 ++---- ...iginWithoutCredentialsCorsConfigurer.java} | 2 +- .../TraceIdInitializeServletListener.java | 6 +-- .../AuthenticationInterceptor.java | 8 ++- .../web/utils/ContentCachingServletUtils.java | 40 ++++++++++++++ ...ultPropertiesEnvironmentPostProcessor.java | 1 + 11 files changed, 172 insertions(+), 74 deletions(-) create mode 100644 commons/web-common/src/main/java/com/schbrain/common/web/AuthenticationConfiguration.java create mode 100644 commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java create mode 100644 commons/web-common/src/main/java/com/schbrain/common/web/ServletComponentConfiguration.java rename commons/web-common/src/main/java/com/schbrain/common/web/servlet/{AllowAllCorsConfigurer.java => AllowAnyOriginWithoutCredentialsCorsConfigurer.java} (89%) create mode 100644 commons/web-common/src/main/java/com/schbrain/common/web/utils/ContentCachingServletUtils.java diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/AuthenticationConfiguration.java b/commons/web-common/src/main/java/com/schbrain/common/web/AuthenticationConfiguration.java new file mode 100644 index 0000000..e486e29 --- /dev/null +++ b/commons/web-common/src/main/java/com/schbrain/common/web/AuthenticationConfiguration.java @@ -0,0 +1,24 @@ +package com.schbrain.common.web; + +import com.schbrain.common.web.support.authentication.AuthenticationInterceptor; +import com.schbrain.common.web.support.authentication.Authenticator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author liaozan + * @since 2023-05-08 + */ +@Configuration(proxyBeanMethods = false) +public class AuthenticationConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnBean(Authenticator.class) + public AuthenticationInterceptor defaultAuthenticationInterceptor(Authenticator authenticator) { + return new AuthenticationInterceptor(authenticator); + } + +} \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java b/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java new file mode 100644 index 0000000..7d9bbb4 --- /dev/null +++ b/commons/web-common/src/main/java/com/schbrain/common/web/ExceptionHandingConfiguration.java @@ -0,0 +1,30 @@ +package com.schbrain.common.web; + +import com.schbrain.common.web.exception.*; +import com.schbrain.common.web.properties.WebProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author liaozan + * @since 2023-05-08 + */ +@Configuration(proxyBeanMethods = false) +public class ExceptionHandingConfiguration { + + @Bean + @ConditionalOnMissingBean + public GlobalExceptionHandler defaultGlobalExceptionHandler() { + return new DefaultGlobalExceptionHandler(); + } + + @Bean + @ConditionalOnMissingBean + @ConditionalOnBean(GlobalExceptionHandler.class) + public ExceptionHandlerWebMcvConfigurer defaultExceptionHandlerWebMcvConfigurer(WebProperties webProperties, GlobalExceptionHandler exceptionHandler) { + return new ExceptionHandlerWebMcvConfigurer(webProperties, exceptionHandler); + } + +} \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/ServletComponentConfiguration.java b/commons/web-common/src/main/java/com/schbrain/common/web/ServletComponentConfiguration.java new file mode 100644 index 0000000..a8efd56 --- /dev/null +++ b/commons/web-common/src/main/java/com/schbrain/common/web/ServletComponentConfiguration.java @@ -0,0 +1,39 @@ +package com.schbrain.common.web; + +import com.schbrain.common.web.log.RequestLoggingFilter; +import com.schbrain.common.web.properties.WebProperties; +import com.schbrain.common.web.servlet.CharacterEncodingServletContextInitializer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.RequestContextFilter; + +/** + * @author liaozan + * @since 2023-05-08 + */ +@Configuration(proxyBeanMethods = false) +public class ServletComponentConfiguration { + + @Bean + @ConditionalOnMissingBean + public CharacterEncodingServletContextInitializer characterEncodingServletContextInitializer(WebProperties webProperties) { + return new CharacterEncodingServletContextInitializer(webProperties.getEncoding()); + } + + @Bean + @ConditionalOnMissingBean + public RequestContextFilter requestContextFilter() { + OrderedRequestContextFilter requestContextFilter = new OrderedRequestContextFilter(); + requestContextFilter.setThreadContextInheritable(true); + return requestContextFilter; + } + + @Bean + @ConditionalOnMissingBean + public RequestLoggingFilter requestLoggingFilter(WebProperties properties) { + return new RequestLoggingFilter(properties); + } + +} \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/WebCommonAutoConfiguration.java b/commons/web-common/src/main/java/com/schbrain/common/web/WebCommonAutoConfiguration.java index dbfd23b..933c8b7 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/WebCommonAutoConfiguration.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/WebCommonAutoConfiguration.java @@ -1,22 +1,20 @@ package com.schbrain.common.web; import com.schbrain.common.web.argument.BodyParamArgumentResolverWebMvcConfigurer; -import com.schbrain.common.web.exception.*; -import com.schbrain.common.web.log.RequestLoggingFilter; import com.schbrain.common.web.properties.WebProperties; import com.schbrain.common.web.result.ResponseBodyHandler; -import com.schbrain.common.web.servlet.*; -import com.schbrain.common.web.support.authentication.AuthenticationInterceptor; -import com.schbrain.common.web.support.authentication.Authenticator; +import com.schbrain.common.web.servlet.AllowAnyOriginWithoutCredentialsCorsConfigurer; +import com.schbrain.common.web.servlet.TraceIdInitializeServletListener; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; -import org.springframework.boot.autoconfigure.condition.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.*; import org.springframework.web.client.RestTemplate; import java.util.List; @@ -26,29 +24,11 @@ import java.util.List; * @since 2021/11/19 */ @AutoConfiguration -@ConditionalOnWebApplication +@ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(WebProperties.class) +@Import({AuthenticationConfiguration.class, ExceptionHandingConfiguration.class, ServletComponentConfiguration.class}) public class WebCommonAutoConfiguration { - @Bean - @ConditionalOnMissingBean - @ConditionalOnBean(Authenticator.class) - public AuthenticationInterceptor defaultAuthenticationInterceptor(Authenticator authenticator) { - return new AuthenticationInterceptor(authenticator); - } - - @Bean - @ConditionalOnMissingBean - public GlobalExceptionHandler defaultGlobalExceptionHandler() { - return new DefaultGlobalExceptionHandler(); - } - - @Bean - @ConditionalOnMissingBean - public ExceptionHandlerWebMcvConfigurer defaultExceptionHandlerWebMcvConfigurer(WebProperties webProperties, GlobalExceptionHandler exceptionHandler) { - return new ExceptionHandlerWebMcvConfigurer(webProperties, exceptionHandler); - } - @Bean @ConditionalOnMissingBean public BodyParamArgumentResolverWebMvcConfigurer defaultBodyParamArgumentResolverWebMvcConfigurer() { @@ -81,20 +61,8 @@ public class WebCommonAutoConfiguration { @Bean @ConditionalOnMissingBean - public CharacterEncodingServletContextInitializer characterEncodingServletContextInitializer(WebProperties webProperties) { - return new CharacterEncodingServletContextInitializer(webProperties.getEncoding()); - } - - @Bean - @ConditionalOnMissingBean - public RequestLoggingFilter requestLoggingFilter(WebProperties properties) { - return new RequestLoggingFilter(properties); - } - - @Bean - @ConditionalOnMissingBean - public AllowAllCorsConfigurer allowAllCorsConfigurer() { - return new AllowAllCorsConfigurer(); + public AllowAnyOriginWithoutCredentialsCorsConfigurer allowAnyOriginWithoutCredentialsCorsConfigurer() { + return new AllowAnyOriginWithoutCredentialsCorsConfigurer(); } } \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/argument/BodyParamMethodArgumentResolver.java b/commons/web-common/src/main/java/com/schbrain/common/web/argument/BodyParamMethodArgumentResolver.java index 61d7559..01b5c1a 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/argument/BodyParamMethodArgumentResolver.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/argument/BodyParamMethodArgumentResolver.java @@ -7,14 +7,14 @@ import com.schbrain.common.web.annotation.BodyParam; import lombok.Setter; import org.springframework.core.MethodParameter; import org.springframework.util.Assert; -import org.springframework.util.StreamUtils; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver; -import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import java.io.IOException; -import java.io.InputStream; + +import static com.schbrain.common.web.utils.ContentCachingServletUtils.wrapRequestIfRequired; +import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST; /** * @author liaozan @@ -23,7 +23,7 @@ import java.io.InputStream; @Setter public class BodyParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { - private static final String METHOD_BODY_CACHE_KEY = BodyParamMethodArgumentResolver.class.getName() + ".bodyParamCache"; + private static final String REQUEST_BODY_CACHE = BodyParamMethodArgumentResolver.class.getName() + ".requestBodyCache"; private ObjectMapper objectMapper; @@ -48,10 +48,9 @@ public class BodyParamMethodArgumentResolver extends AbstractNamedValueMethodArg } @Override - @Nullable protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { - JsonNode paramNode = getParamNode(request); - JsonNode parameterValue = paramNode.get(name); + JsonNode requestBody = getRequestBody(request); + JsonNode parameterValue = requestBody.get(name); if (parameterValue == null || parameterValue.isNull()) { return null; } @@ -59,16 +58,14 @@ public class BodyParamMethodArgumentResolver extends AbstractNamedValueMethodArg return objectMapper.convertValue(parameterValue, parameterType); } - private JsonNode getParamNode(NativeWebRequest nativeWebRequest) throws IOException { - HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class); - Assert.state(request != null, "request must not be null"); - JsonNode paramNode = (JsonNode) request.getAttribute(METHOD_BODY_CACHE_KEY); - if (paramNode == null) { - InputStream inputStream = StreamUtils.nonClosing(request.getInputStream()); - paramNode = objectMapper.readTree(inputStream); - request.setAttribute(METHOD_BODY_CACHE_KEY, paramNode); + private JsonNode getRequestBody(NativeWebRequest nativeWebRequest) throws IOException { + JsonNode requestBody = (JsonNode) nativeWebRequest.getAttribute(REQUEST_BODY_CACHE, SCOPE_REQUEST); + if (requestBody == null) { + HttpServletRequest request = wrapRequestIfRequired(nativeWebRequest.getNativeRequest(HttpServletRequest.class)); + requestBody = objectMapper.readTree(request.getInputStream()); + nativeWebRequest.setAttribute(REQUEST_BODY_CACHE, requestBody, SCOPE_REQUEST); } - return paramNode; + return requestBody; } } \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/log/RequestLoggingFilter.java b/commons/web-common/src/main/java/com/schbrain/common/web/log/RequestLoggingFilter.java index 2fda0ce..dcdb11f 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/log/RequestLoggingFilter.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/log/RequestLoggingFilter.java @@ -1,6 +1,6 @@ package com.schbrain.common.web.log; -import cn.hutool.core.text.StrPool; +import cn.hutool.core.text.CharPool; import cn.hutool.core.util.ArrayUtil; import com.schbrain.common.web.properties.WebProperties; import lombok.extern.slf4j.Slf4j; @@ -19,6 +19,8 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; +import static com.schbrain.common.web.utils.ContentCachingServletUtils.wrapRequestIfRequired; + /** * 请求日志拦截器 */ @@ -44,6 +46,7 @@ public class RequestLoggingFilter extends OncePerRequestFilter implements Ordere } request = wrapRequestIfRequired(request); + long startTime = System.currentTimeMillis(); try { chain.doFilter(request, response); @@ -67,7 +70,7 @@ public class RequestLoggingFilter extends OncePerRequestFilter implements Ordere String queryString = request.getQueryString(); String body = getRequestBody(request); StringBuilder builder = new StringBuilder(); - builder.append("requestUri: ").append(method).append(StrPool.C_SPACE).append(requestUri); + builder.append("requestUri: ").append(method).append(CharPool.SPACE).append(requestUri); if (StringUtils.hasText(queryString)) { builder.append(", queryString: ").append(queryString); } @@ -98,12 +101,4 @@ public class RequestLoggingFilter extends OncePerRequestFilter implements Ordere } } - protected HttpServletRequest wrapRequestIfRequired(HttpServletRequest request) { - if (request instanceof ContentCachingRequestWrapper) { - return request; - } else { - return new ContentCachingRequestWrapper(request); - } - } - } \ No newline at end of file diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/servlet/AllowAllCorsConfigurer.java b/commons/web-common/src/main/java/com/schbrain/common/web/servlet/AllowAnyOriginWithoutCredentialsCorsConfigurer.java similarity index 89% rename from commons/web-common/src/main/java/com/schbrain/common/web/servlet/AllowAllCorsConfigurer.java rename to commons/web-common/src/main/java/com/schbrain/common/web/servlet/AllowAnyOriginWithoutCredentialsCorsConfigurer.java index 320aa0f..7b3efdf 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/servlet/AllowAllCorsConfigurer.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/servlet/AllowAnyOriginWithoutCredentialsCorsConfigurer.java @@ -10,7 +10,7 @@ import java.time.Duration; * @author liaozan * @since 2022/11/19 */ -public class AllowAllCorsConfigurer implements WebMvcConfigurer { +public class AllowAnyOriginWithoutCredentialsCorsConfigurer implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/servlet/TraceIdInitializeServletListener.java b/commons/web-common/src/main/java/com/schbrain/common/web/servlet/TraceIdInitializeServletListener.java index c9f57c8..759fcd7 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/servlet/TraceIdInitializeServletListener.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/servlet/TraceIdInitializeServletListener.java @@ -1,26 +1,24 @@ package com.schbrain.common.web.servlet; import com.schbrain.common.util.TraceIdUtils; -import org.springframework.web.context.request.RequestContextListener; import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; /** * @author liaozan * @since 2021/12/8 */ -public class TraceIdInitializeServletListener extends RequestContextListener { +public class TraceIdInitializeServletListener implements ServletRequestListener { @Override public void requestInitialized(ServletRequestEvent event) { - super.requestInitialized(event); // Make sure the traceId is initialized TraceIdUtils.get(); } @Override public void requestDestroyed(ServletRequestEvent event) { - super.requestDestroyed(event); // Make sure the traceId can be cleared TraceIdUtils.clear(); } diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/support/authentication/AuthenticationInterceptor.java b/commons/web-common/src/main/java/com/schbrain/common/web/support/authentication/AuthenticationInterceptor.java index 3375ccf..678339e 100644 --- a/commons/web-common/src/main/java/com/schbrain/common/web/support/authentication/AuthenticationInterceptor.java +++ b/commons/web-common/src/main/java/com/schbrain/common/web/support/authentication/AuthenticationInterceptor.java @@ -6,6 +6,7 @@ import com.schbrain.common.web.result.ResponseDTO; import com.schbrain.common.web.support.BaseHandlerInterceptor; import lombok.Data; import lombok.EqualsAndHashCode; +import org.springframework.core.Ordered; import org.springframework.http.MediaType; import org.springframework.util.Assert; import org.springframework.web.method.HandlerMethod; @@ -22,7 +23,7 @@ import static com.schbrain.common.constants.ResponseCodeConstants.LOGIN_REQUIRED */ @Data @EqualsAndHashCode(callSuper = true) -public class AuthenticationInterceptor extends BaseHandlerInterceptor { +public class AuthenticationInterceptor extends BaseHandlerInterceptor implements Ordered { private Authenticator authenticator; @@ -31,6 +32,11 @@ public class AuthenticationInterceptor extends BaseHandlerInterceptor { this.authenticator = authenticator; } + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + @Override protected boolean preHandle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handler) { boolean validated = authenticator.validate(request, response, handler); diff --git a/commons/web-common/src/main/java/com/schbrain/common/web/utils/ContentCachingServletUtils.java b/commons/web-common/src/main/java/com/schbrain/common/web/utils/ContentCachingServletUtils.java new file mode 100644 index 0000000..4fd71d1 --- /dev/null +++ b/commons/web-common/src/main/java/com/schbrain/common/web/utils/ContentCachingServletUtils.java @@ -0,0 +1,40 @@ +package com.schbrain.common.web.utils; + +import org.springframework.util.Assert; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author liaozan + * @since 2023-05-08 + */ +public class ContentCachingServletUtils { + + /** + * Make request content cacheable to avoid stream closed error after inputStream closed + */ + public static HttpServletRequest wrapRequestIfRequired(HttpServletRequest request) { + Assert.notNull(request, "request must not be null"); + if (request instanceof ContentCachingRequestWrapper) { + return request; + } else { + return new ContentCachingRequestWrapper(request); + } + } + + /** + * Make response content cacheable to avoid stream closed error after outputStream closed + */ + public static HttpServletResponse wrapResponseIfRequired(HttpServletResponse response) { + Assert.notNull(response, "response must not be null"); + if (response instanceof ContentCachingResponseWrapper) { + return response; + } else { + return new ContentCachingResponseWrapper(response); + } + } + +} \ No newline at end of file diff --git a/support/schbrain-spring-support/src/main/java/com/schbrain/framework/support/spring/defaults/DefaultPropertiesEnvironmentPostProcessor.java b/support/schbrain-spring-support/src/main/java/com/schbrain/framework/support/spring/defaults/DefaultPropertiesEnvironmentPostProcessor.java index a24d15f..25b9182 100644 --- a/support/schbrain-spring-support/src/main/java/com/schbrain/framework/support/spring/defaults/DefaultPropertiesEnvironmentPostProcessor.java +++ b/support/schbrain-spring-support/src/main/java/com/schbrain/framework/support/spring/defaults/DefaultPropertiesEnvironmentPostProcessor.java @@ -42,6 +42,7 @@ public class DefaultPropertiesEnvironmentPostProcessor extends LoggerAwareEnviro public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Map defaultProperties = new HashMap<>(); // management + defaultProperties.put("management.trace.http.enabled", false); defaultProperties.put("management.endpoints.web.exposure.include", "*"); defaultProperties.put("management.endpoints.enabled-by-default", true); defaultProperties.put("management.endpoint.health.show-details", Show.ALWAYS); -- GitLab