configurer

This commit is contained in:
sxu 2025-03-14 14:15:54 +08:00
parent ee9c8f1401
commit b8e44254aa
14 changed files with 285 additions and 17 deletions

View File

@ -4,6 +4,7 @@ import com.google.common.collect.Maps;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import lombok.Generated;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(
@ -12,41 +13,150 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class ApiProperties {
public static final String PREFIX = "api";
private String duplicateRequestMappingWithPrefix;
private boolean defaultToForceUseSecurity = false;
private Set<String> wrapperPackageInclude = new HashSet();
private Set<String> wrapperPackageExclude = new HashSet();
private Map<String, Set<String>> contextPath = Maps.newLinkedHashMap();
@Generated
public String getDuplicateRequestMappingWithPrefix() {
return this.duplicateRequestMappingWithPrefix;
}
@Generated
public boolean isDefaultToForceUseSecurity() {
return this.defaultToForceUseSecurity;
}
@Generated
public Set<String> getWrapperPackageInclude() {
return this.wrapperPackageInclude;
}
@Generated
public Set<String> getWrapperPackageExclude() {
return this.wrapperPackageExclude;
}
@Generated
public Map<String, Set<String>> getContextPath() {
return this.contextPath;
}
@Generated
public void setDuplicateRequestMappingWithPrefix(final String duplicateRequestMappingWithPrefix) {
this.duplicateRequestMappingWithPrefix = duplicateRequestMappingWithPrefix;
}
@Generated
public void setDefaultToForceUseSecurity(final boolean defaultToForceUseSecurity) {
this.defaultToForceUseSecurity = defaultToForceUseSecurity;
}
@Generated
public void setWrapperPackageInclude(final Set<String> wrapperPackageInclude) {
this.wrapperPackageInclude = wrapperPackageInclude;
}
@Generated
public void setWrapperPackageExclude(final Set<String> wrapperPackageExclude) {
this.wrapperPackageExclude = wrapperPackageExclude;
}
@Generated
public void setContextPath(final Map<String, Set<String>> contextPath) {
this.contextPath = contextPath;
}
@Generated
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof ApiProperties)) {
return false;
} else {
ApiProperties other = (ApiProperties)o;
if (!other.canEqual(this)) {
return false;
} else if (this.isDefaultToForceUseSecurity() != other.isDefaultToForceUseSecurity()) {
return false;
} else {
label61: {
Object this$duplicateRequestMappingWithPrefix = this.getDuplicateRequestMappingWithPrefix();
Object other$duplicateRequestMappingWithPrefix = other.getDuplicateRequestMappingWithPrefix();
if (this$duplicateRequestMappingWithPrefix == null) {
if (other$duplicateRequestMappingWithPrefix == null) {
break label61;
}
} else if (this$duplicateRequestMappingWithPrefix.equals(other$duplicateRequestMappingWithPrefix)) {
break label61;
}
return false;
}
label54: {
Object this$wrapperPackageInclude = this.getWrapperPackageInclude();
Object other$wrapperPackageInclude = other.getWrapperPackageInclude();
if (this$wrapperPackageInclude == null) {
if (other$wrapperPackageInclude == null) {
break label54;
}
} else if (this$wrapperPackageInclude.equals(other$wrapperPackageInclude)) {
break label54;
}
return false;
}
Object this$wrapperPackageExclude = this.getWrapperPackageExclude();
Object other$wrapperPackageExclude = other.getWrapperPackageExclude();
if (this$wrapperPackageExclude == null) {
if (other$wrapperPackageExclude != null) {
return false;
}
} else if (!this$wrapperPackageExclude.equals(other$wrapperPackageExclude)) {
return false;
}
Object this$contextPath = this.getContextPath();
Object other$contextPath = other.getContextPath();
if (this$contextPath == null) {
if (other$contextPath != null) {
return false;
}
} else if (!this$contextPath.equals(other$contextPath)) {
return false;
}
return true;
}
}
}
@Generated
protected boolean canEqual(final Object other) {
return other instanceof ApiProperties;
}
@Generated
public int hashCode() {
int PRIME = true;
int result = 1;
result = result * 59 + (this.isDefaultToForceUseSecurity() ? 79 : 97);
Object $duplicateRequestMappingWithPrefix = this.getDuplicateRequestMappingWithPrefix();
result = result * 59 + ($duplicateRequestMappingWithPrefix == null ? 43 : $duplicateRequestMappingWithPrefix.hashCode());
Object $wrapperPackageInclude = this.getWrapperPackageInclude();
result = result * 59 + ($wrapperPackageInclude == null ? 43 : $wrapperPackageInclude.hashCode());
Object $wrapperPackageExclude = this.getWrapperPackageExclude();
result = result * 59 + ($wrapperPackageExclude == null ? 43 : $wrapperPackageExclude.hashCode());
Object $contextPath = this.getContextPath();
result = result * 59 + ($contextPath == null ? 43 : $contextPath.hashCode());
return result;
}
@Generated
public String toString() {
return "ApiProperties(duplicateRequestMappingWithPrefix=" + this.getDuplicateRequestMappingWithPrefix() + ", defaultToForceUseSecurity=" + this.isDefaultToForceUseSecurity() + ", wrapperPackageInclude=" + this.getWrapperPackageInclude() + ", wrapperPackageExclude=" + this.getWrapperPackageExclude() + ", contextPath=" + this.getContextPath() + ")";
}
}

View File

@ -1,5 +1,7 @@
package net.xnzn.framework.config.exception;
import lombok.Generated;
public class BizException extends Exception implements CodedException {
private Integer code;
@ -34,6 +36,7 @@ public class BizException extends Exception implements CodedException {
return null;
}
@Generated
public Integer getCode() {
return this.code;
}

View File

@ -1,5 +1,7 @@
package net.xnzn.framework.config.exception;
import lombok.Generated;
public class BizRuntimeException extends RuntimeException implements CodedException {
private Integer code;
@ -34,6 +36,7 @@ public class BizRuntimeException extends RuntimeException implements CodedExcept
return null;
}
@Generated
public Integer getCode() {
return this.code;
}

View File

@ -5,6 +5,7 @@ import cn.hutool.core.text.CharSequenceUtil;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.stream.Collectors;
import lombok.Generated;
import net.xnzn.framework.config.response.ResponseWrapper;
import net.xnzn.framework.data.dataset.datasource.RoutingDataSource;
import net.xnzn.framework.data.dataset.exception.ChooseDatasetException;
@ -25,6 +26,7 @@ import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.MethodArgumentNotValidException;
@ -37,6 +39,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExcep
@ControllerAdvice
@Order(-2147483598)
public class ExceptionHandlerConfiguration extends ResponseEntityExceptionHandler {
@Generated
private static final Logger log = LoggerFactory.getLogger(ExceptionHandlerConfiguration.class);
private static ResponseEntity<Object> handleException(Exception ex, @Nullable Object body, HttpHeaders headers, int status, WebRequest request) {
@ -45,7 +48,7 @@ public class ExceptionHandlerConfiguration extends ResponseEntityExceptionHandle
}
String message = logException(ex, status);
ResponseWrapper<?> fail = ResponseWrapper.fails(ex instanceof CodedException ? ((CodedException)ex).getCode() : null, body == null ? message : body.toString());
ResponseWrapper<?> fail = ResponseWrapper.fails(ex instanceof CodedException ? ((CodedException)ex).getCode() : null, body != null && !(body instanceof ProblemDetail) ? body.toString() : message);
return new ResponseEntity(fail, headers, status);
}

View File

@ -0,0 +1,25 @@
package net.xnzn.framework.config.json;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeFeature;
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.function.BiFunction;
public class InstantsDeserializer extends InstantDeserializer<Instant> {
private static final boolean DEFAULT_NORMALIZE_ZONE_ID;
private static final boolean DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS;
public InstantsDeserializer(DateTimeFormatter pattern) {
super(Instant.class, pattern, Instant::from, (a) -> {
return Instant.ofEpochMilli(a.value);
}, (a) -> {
return Instant.ofEpochSecond(a.integer, (long)a.fraction);
}, (BiFunction)null, true, DEFAULT_NORMALIZE_ZONE_ID, DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS);
}
static {
DEFAULT_NORMALIZE_ZONE_ID = JavaTimeFeature.NORMALIZE_DESERIALIZED_ZONE_ID.enabledByDefault();
DEFAULT_ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS = JavaTimeFeature.ALWAYS_ALLOW_STRINGIFIED_DATE_TIMESTAMPS.enabledByDefault();
}
}

View File

@ -0,0 +1,10 @@
package net.xnzn.framework.config.json;
import com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer;
import java.time.format.DateTimeFormatter;
public class InstantsSerializer extends InstantSerializer {
public InstantsSerializer(DateTimeFormatter defaultFormat) {
super(InstantSerializer.INSTANCE, false, defaultFormat);
}
}

View File

@ -13,6 +13,7 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
@ -24,7 +25,6 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.TimeZone;
import net.xnzn.framework.config.json.serializer.InstantsSerializer;
import net.xnzn.framework.secure.WebContext;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -32,8 +32,6 @@ import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilde
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import javax.servlet.http.HttpServletRequest;
@AutoConfiguration(
before = {JacksonAutoConfiguration.class}
)
@ -58,6 +56,7 @@ public class JacksonConfiguration {
this.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
this.addDeserializer(Instant.class, new InstantsDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
}
}});
};

View File

@ -7,6 +7,7 @@ import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.Generated;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
@ -15,6 +16,7 @@ import org.springframework.web.filter.CommonsRequestLoggingFilter;
import org.springframework.web.util.WebUtils;
public class RequestLoggingFilter extends CommonsRequestLoggingFilter {
@Generated
private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);
public static final String SHOULD_LOG = RequestLoggingFilter.class.getName() + ".SHOULD_LOG";

View File

@ -1,6 +1,7 @@
package net.xnzn.framework.config.response;
import cn.hutool.core.text.CharSequenceUtil;
import lombok.Generated;
import net.xnzn.framework.config.request.RequestLoggingFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -16,6 +17,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@Order(-2147483598)
@RestControllerAdvice
public class ResponseLoggingAdvice implements ResponseBodyAdvice<Object> {
@Generated
private static final Logger log = LoggerFactory.getLogger(ResponseLoggingAdvice.class);
public boolean supports(MethodParameter returnType, Class converterType) {

View File

@ -1,14 +1,15 @@
package net.xnzn.framework.config.response;
//import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import lombok.Generated;
public class ResponseWrapper<T> implements Serializable {
//@ApiModelProperty("状态码")
@ApiModelProperty("状态码")
protected Integer code = 10000;
//@ApiModelProperty("返回信息")
@ApiModelProperty("返回信息")
protected String msg = "成功";
//@ApiModelProperty("返回数据")
@ApiModelProperty("返回数据")
protected T data;
public ResponseWrapper(T data) {
@ -22,30 +23,110 @@ public class ResponseWrapper<T> implements Serializable {
return apiResult;
}
@Generated
public Integer getCode() {
return this.code;
}
@Generated
public String getMsg() {
return this.msg;
}
@Generated
public T getData() {
return this.data;
}
@Generated
public void setCode(final Integer code) {
this.code = code;
}
@Generated
public void setMsg(final String msg) {
this.msg = msg;
}
@Generated
public void setData(final T data) {
this.data = data;
}
@Generated
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof ResponseWrapper)) {
return false;
} else {
ResponseWrapper<?> other = (ResponseWrapper)o;
if (!other.canEqual(this)) {
return false;
} else {
label47: {
Object this$code = this.getCode();
Object other$code = other.getCode();
if (this$code == null) {
if (other$code == null) {
break label47;
}
} else if (this$code.equals(other$code)) {
break label47;
}
return false;
}
Object this$msg = this.getMsg();
Object other$msg = other.getMsg();
if (this$msg == null) {
if (other$msg != null) {
return false;
}
} else if (!this$msg.equals(other$msg)) {
return false;
}
Object this$data = this.getData();
Object other$data = other.getData();
if (this$data == null) {
if (other$data != null) {
return false;
}
} else if (!this$data.equals(other$data)) {
return false;
}
return true;
}
}
}
@Generated
protected boolean canEqual(final Object other) {
return other instanceof ResponseWrapper;
}
@Generated
public int hashCode() {
int PRIME = true;
int result = 1;
Object $code = this.getCode();
result = result * 59 + ($code == null ? 43 : $code.hashCode());
Object $msg = this.getMsg();
result = result * 59 + ($msg == null ? 43 : $msg.hashCode());
Object $data = this.getData();
result = result * 59 + ($data == null ? 43 : $data.hashCode());
return result;
}
@Generated
public String toString() {
return "ResponseWrapper(code=" + this.getCode() + ", msg=" + this.getMsg() + ", data=" + this.getData() + ")";
}
@Generated
public ResponseWrapper() {
}
}

View File

@ -1,8 +1,8 @@
package net.xnzn.framework.config.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.lang.annotation.Annotation;
import java.util.Set;
import lombok.Generated;
import net.xnzn.framework.config.ApiProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -20,6 +20,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@Order(Integer.MIN_VALUE)
@RestControllerAdvice
public class ResponseWrapperAdvice implements ResponseBodyAdvice<Object> {
@Generated
private static final Logger log = LoggerFactory.getLogger(ResponseWrapperAdvice.class);
private final Set<String> includePackage;
private final Set<String> excludePackage;
@ -34,13 +35,13 @@ public class ResponseWrapperAdvice implements ResponseBodyAdvice<Object> {
public boolean supports(MethodParameter returnType, Class converterType) {
return this.excludePackage.stream().noneMatch((pkg) -> {
return returnType.getDeclaringClass().getName().startsWith(pkg);
}) && !this.isAnnotationPresent(returnType, ResponseWrapperByPass.class) && this.includePackage.stream().anyMatch((pkg) -> {
}) && !this.isAnnotationPresent(returnType) && this.includePackage.stream().anyMatch((pkg) -> {
return returnType.getDeclaringClass().getName().startsWith(pkg);
}) && returnType.getMethod() != null && !ResponseWrapper.class.isAssignableFrom(returnType.getMethod().getReturnType());
}
private boolean isAnnotationPresent(MethodParameter returnType, Class<? extends Annotation> annotationClass) {
return AnnotationUtils.findAnnotation(returnType.getDeclaringClass(), annotationClass) != null || returnType.getMethod() != null && AnnotationUtils.findAnnotation(returnType.getMethod(), annotationClass) != null;
private boolean isAnnotationPresent(MethodParameter returnType) {
return AnnotationUtils.findAnnotation(returnType.getDeclaringClass(), ResponseWrapperByPass.class) != null || returnType.getMethod() != null && AnnotationUtils.findAnnotation(returnType.getMethod(), ResponseWrapperByPass.class) != null;
}
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {

View File

@ -21,7 +21,7 @@ public class ApiPathConfigurer implements WebMvcConfigurer, WebMvcRegistrations
}
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new EnhancerRequestMappingHandlerMapping(this.apiPathProperties.getDuplicateRequestMappingWithPrefix());
return new EnhancerRequestMappingHandlerMapping(this.apiPathProperties.getDuplicateRequestMappingWithPrefix(), this.apiPathProperties.isDefaultToForceUseSecurity());
}
public void configurePathMatch(PathMatchConfigurer configurer) {

View File

@ -4,25 +4,31 @@ import cn.hutool.core.text.CharSequenceUtil;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.Generated;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPatternParser;
public class EnhancerRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Generated
private static final Logger log = LoggerFactory.getLogger(EnhancerRequestMappingHandlerMapping.class);
public static final PathPatternParser pathPatternParser = new PathPatternParser();
private final String duplicateRequestMappingWithPrefix;
private final boolean defaultToForceUseSecurity;
public EnhancerRequestMappingHandlerMapping(String duplicateRequestMappingWithPrefix) {
public EnhancerRequestMappingHandlerMapping(String duplicateRequestMappingWithPrefix, boolean defaultToForceUseSecurity) {
this.duplicateRequestMappingWithPrefix = duplicateRequestMappingWithPrefix;
this.defaultToForceUseSecurity = defaultToForceUseSecurity;
}
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
try {
if (CharSequenceUtil.isNotBlank(this.duplicateRequestMappingWithPrefix)) {
boolean securityEscape = this.isSecurityEscape(method);
Set definedPatterns;
Set addonPatterns;
if (mapping.getPatternsCondition() != null) {
@ -30,24 +36,36 @@ public class EnhancerRequestMappingHandlerMapping extends RequestMappingHandlerM
addonPatterns = (Set)definedPatterns.stream().map((pattern) -> {
return this.duplicateRequestMappingWithPrefix + pattern;
}).collect(Collectors.toSet());
if (this.defaultToForceUseSecurity != securityEscape) {
definedPatterns.clear();
}
definedPatterns.addAll(addonPatterns);
} else if (mapping.getPathPatternsCondition() != null) {
definedPatterns = mapping.getPathPatternsCondition().getPatterns();
addonPatterns = (Set)mapping.getPathPatternsCondition().getPatternValues().stream().map((pattern) -> {
return pathPatternParser.parse(this.duplicateRequestMappingWithPrefix + pattern);
}).collect(Collectors.toSet());
if (this.defaultToForceUseSecurity != securityEscape) {
definedPatterns.clear();
}
definedPatterns.addAll(addonPatterns);
}
}
super.registerHandlerMethod(handler, method, mapping);
} catch (IllegalStateException var6) {
if (ArrayUtils.isEmpty(var6.getStackTrace()) || !"validateMethodMapping".equals(var6.getStackTrace()[0].getMethodName())) {
throw var6;
} catch (IllegalStateException var7) {
if (ArrayUtils.isEmpty(var7.getStackTrace()) || !"validateMethodMapping".equals(var7.getStackTrace()[0].getMethodName())) {
throw var7;
}
log.warn("requestMapping '{}' against '{}' is override", mapping, method);
}
}
private boolean isSecurityEscape(Method method) {
return AnnotatedElementUtils.findMergedAnnotation(method, SecurityEscape.class) != null || AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), SecurityEscape.class) != null;
}
}

View File

@ -0,0 +1,11 @@
package net.xnzn.framework.config.spring;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SecurityEscape {
}