mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-08 08:54:07 +08:00
feat: 适配spring-boot3
This commit is contained in:
2
.mvn/wrapper/maven-wrapper.properties
vendored
2
.mvn/wrapper/maven-wrapper.properties
vendored
@@ -1 +1 @@
|
||||
distributionUrl=https://downloads.apache.org/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip
|
||||
distributionUrl=https://archive.apache.org/dist/maven/maven-3/3.9.3/binaries/apache-maven-3.9.3-bin.zip
|
||||
@@ -47,8 +47,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
* @author zhouhao
|
||||
* @since 3.0
|
||||
*/
|
||||
@Deprecated
|
||||
public interface DataAccessController {
|
||||
/**
|
||||
* 执行权限控制
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.hswebframework.web.authorization.simple;
|
||||
import lombok.*;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -16,6 +17,7 @@ import java.util.Map;
|
||||
@Builder
|
||||
public class SimpleUser implements User {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 2194541828191869091L;
|
||||
|
||||
private String id;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.hswebframework.web.authorization.simple.builder;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.builder.DataAccessConfigBuilder;
|
||||
import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFactory;
|
||||
import org.hswebframework.web.authorization.simple.*;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
module hsweb.authorization.api {
|
||||
requires spring.core;
|
||||
requires hsweb.core;
|
||||
requires spring.beans;
|
||||
requires spring.boot.autoconfigure;
|
||||
requires spring.context;
|
||||
requires spring.boot;
|
||||
requires static spring.data.redis;
|
||||
requires reactor.core;
|
||||
requires static lombok;
|
||||
requires fastjson;
|
||||
requires commons.collections;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires jakarta.annotation;
|
||||
requires org.slf4j;
|
||||
|
||||
|
||||
exports org.hswebframework.web.authorization;
|
||||
exports org.hswebframework.web.authorization.access;
|
||||
exports org.hswebframework.web.authorization.annotation;
|
||||
exports org.hswebframework.web.authorization.token.redis;
|
||||
exports org.hswebframework.web.authorization.token.event;
|
||||
exports org.hswebframework.web.authorization.builder;
|
||||
exports org.hswebframework.web.authorization.define;
|
||||
exports org.hswebframework.web.authorization.dimension;
|
||||
exports org.hswebframework.web.authorization.events;
|
||||
exports org.hswebframework.web.authorization.exception;
|
||||
exports org.hswebframework.web.authorization.setting;
|
||||
exports org.hswebframework.web.authorization.simple;
|
||||
exports org.hswebframework.web.authorization.simple.builder;
|
||||
exports org.hswebframework.web.authorization.twofactor.defaults;
|
||||
exports org.hswebframework.web.authorization.twofactor;
|
||||
|
||||
opens org.hswebframework.web.authorization.simple;
|
||||
exports org.hswebframework.web.authorization.token;
|
||||
|
||||
}
|
||||
@@ -20,11 +20,6 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-expands-script</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
@@ -69,8 +64,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
@@ -103,6 +98,25 @@
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-client-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.expressly</groupId>
|
||||
<artifactId>expressly</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -3,33 +3,20 @@ package org.hswebframework.web.authorization.basic.configuration;
|
||||
import org.hswebframework.web.authorization.AuthenticationManager;
|
||||
import org.hswebframework.web.authorization.ReactiveAuthenticationManagerProvider;
|
||||
import org.hswebframework.web.authorization.access.DataAccessController;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
|
||||
import org.hswebframework.web.authorization.basic.embed.EmbedAuthenticationProperties;
|
||||
import org.hswebframework.web.authorization.basic.embed.EmbedReactiveAuthenticationManager;
|
||||
import org.hswebframework.web.authorization.basic.handler.DefaultAuthorizingHandler;
|
||||
import org.hswebframework.web.authorization.basic.handler.UserAllowPermissionHandler;
|
||||
import org.hswebframework.web.authorization.basic.handler.access.DefaultDataAccessController;
|
||||
import org.hswebframework.web.authorization.basic.twofactor.TwoFactorHandlerInterceptorAdapter;
|
||||
import org.hswebframework.web.authorization.basic.web.*;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.*;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 权限控制自动配置类
|
||||
@@ -41,14 +28,10 @@ import java.util.List;
|
||||
@EnableConfigurationProperties(EmbedAuthenticationProperties.class)
|
||||
public class AuthorizingHandlerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public DefaultDataAccessController dataAccessController() {
|
||||
return new DefaultDataAccessController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DefaultAuthorizingHandler authorizingHandler(DataAccessController dataAccessController) {
|
||||
return new DefaultAuthorizingHandler(dataAccessController);
|
||||
public DefaultAuthorizingHandler authorizingHandler() {
|
||||
return new DefaultAuthorizingHandler(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -93,27 +76,6 @@ public class AuthorizingHandlerAutoConfiguration {
|
||||
return new BearerTokenParser();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class DataAccessHandlerProcessor implements BeanPostProcessor {
|
||||
|
||||
@Autowired
|
||||
private DefaultDataAccessController defaultDataAccessController;
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
||||
if (bean instanceof DataAccessHandler) {
|
||||
defaultDataAccessController.addHandler(((DataAccessHandler) bean));
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "hsweb.authorize", name = "basic-authorization", havingValue = "true")
|
||||
@ConditionalOnClass(UserTokenForTypeParser.class)
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public class BasicAuthorizationTokenParser implements UserTokenForTypeParser {
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.hswebframework.web.authorization.basic.configuration;
|
||||
|
||||
import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
|
||||
import org.hswebframework.web.authorization.basic.twofactor.TwoFactorHandlerInterceptorAdapter;
|
||||
import org.hswebframework.web.authorization.basic.web.*;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
|
||||
@@ -15,7 +14,7 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
@AutoConfiguration
|
||||
@@ -64,16 +63,4 @@ public class WebMvcAuthorizingConfiguration {
|
||||
return new SessionIdUserTokenGenerator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "hsweb.authorize.two-factor", name = "enable", havingValue = "true")
|
||||
@Order(100)
|
||||
public WebMvcConfigurer twoFactorHandlerConfigurer(TwoFactorValidatorManager manager) {
|
||||
return new WebMvcConfigurer() {
|
||||
@Override
|
||||
public void addInterceptors(@Nonnull InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new TwoFactorHandlerInterceptorAdapter(manager));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,10 +11,8 @@ import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfi
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import javax.validation.ValidationException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -36,10 +34,6 @@ import java.util.Optional;
|
||||
* permissions:
|
||||
* - id: user-manager
|
||||
* actions: *
|
||||
* dataAccesses:
|
||||
* - action: query
|
||||
* type: DENY_FIELDS
|
||||
* fields: password,salt
|
||||
* </pre>
|
||||
*
|
||||
* @author zhouhao
|
||||
@@ -62,25 +56,9 @@ public class EmbedAuthenticationProperties implements InitializingBean {
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
users.forEach((id, properties) -> {
|
||||
if (StringUtils.isEmpty(properties.getId())) {
|
||||
if (ObjectUtils.isEmpty(properties.getId())) {
|
||||
properties.setId(id);
|
||||
}
|
||||
for (EmbedAuthenticationInfo.PermissionInfo permissionInfo : properties.getPermissions()) {
|
||||
for (Map<String, Object> objectMap : permissionInfo.getDataAccesses()) {
|
||||
for (Map.Entry<String, Object> stringObjectEntry : objectMap.entrySet()) {
|
||||
if (stringObjectEntry.getValue() instanceof Map) {
|
||||
Map<?, ?> mapVal = ((Map) stringObjectEntry.getValue());
|
||||
boolean maybeIsList = mapVal
|
||||
.keySet()
|
||||
.stream()
|
||||
.allMatch(org.hswebframework.utils.StringUtils::isInt);
|
||||
if (maybeIsList) {
|
||||
stringObjectEntry.setValue(mapVal.values());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
authentications.put(id, properties.toAuthentication(dataAccessConfigBuilderFactory));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ public class DefaultAuthorizingHandler implements AuthorizingHandler {
|
||||
public void handleDataAccess(AuthorizingContext context) {
|
||||
|
||||
if (dataAccessController == null) {
|
||||
log.warn("dataAccessController is null,skip result access control!");
|
||||
return;
|
||||
}
|
||||
if (context.getDefinition().getResources() == null) {
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.ezorm.rdb.mapping.ReactiveRepository;
|
||||
import org.hswebframework.utils.ClassUtils;
|
||||
import org.hswebframework.web.aop.MethodInterceptorContext;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Dimension;
|
||||
import org.hswebframework.web.authorization.DimensionType;
|
||||
import org.hswebframework.web.authorization.define.AuthorizeDefinition;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.crud.web.reactive.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DataAccessHandlerContext {
|
||||
|
||||
private Class<?> entityType;
|
||||
|
||||
private ReactiveRepository<?, Object> repository;
|
||||
|
||||
private Authentication authentication;
|
||||
|
||||
private List<Dimension> dimensions;
|
||||
|
||||
private MethodInterceptorContext paramContext;
|
||||
|
||||
private AuthorizeDefinition definition;
|
||||
|
||||
public static DataAccessHandlerContext of(AuthorizingContext context, String type) {
|
||||
DataAccessHandlerContext requestContext = new DataAccessHandlerContext();
|
||||
Authentication authentication = context.getAuthentication();
|
||||
requestContext.setDimensions(authentication.getDimensions(type));
|
||||
requestContext.setAuthentication(context.getAuthentication());
|
||||
requestContext.setParamContext(context.getParamContext());
|
||||
requestContext.setDefinition(context.getDefinition());
|
||||
Object target = context.getParamContext().getTarget();
|
||||
Class entityType = ClassUtils.getGenericType(org.springframework.util.ClassUtils.getUserClass(target));
|
||||
if (entityType != Object.class) {
|
||||
requestContext.setEntityType(entityType);
|
||||
}
|
||||
|
||||
if (target instanceof ReactiveQueryController) {
|
||||
requestContext.setRepository(((ReactiveQueryController) target).getRepository());
|
||||
} else if (target instanceof ReactiveSaveController) {
|
||||
requestContext.setRepository(((ReactiveSaveController) target).getRepository());
|
||||
} else if (target instanceof ReactiveDeleteController) {
|
||||
requestContext.setRepository(((ReactiveDeleteController) target).getRepository());
|
||||
} else if (target instanceof ReactiveServiceQueryController) {
|
||||
requestContext.setRepository(((ReactiveServiceQueryController) target).getService().getRepository());
|
||||
} else if (target instanceof ReactiveServiceSaveController) {
|
||||
requestContext.setRepository(((ReactiveServiceSaveController) target).getService().getRepository());
|
||||
} else if (target instanceof ReactiveServiceDeleteController) {
|
||||
requestContext.setRepository(((ReactiveServiceDeleteController) target).getService().getRepository());
|
||||
}
|
||||
// TODO: 2019-11-18 not reactive implements
|
||||
|
||||
return requestContext;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.access.DataAccessController;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 默认的行级权限控制.通过获取DataAccessHandler进行实际处理
|
||||
*
|
||||
* @author zhouhao
|
||||
* @see DataAccessHandler
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class DefaultDataAccessController implements DataAccessController {
|
||||
|
||||
private DataAccessController parent;
|
||||
|
||||
private List<DataAccessHandler> handlers = new LinkedList<>();
|
||||
|
||||
public DefaultDataAccessController() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public DefaultDataAccessController(DataAccessController parent) {
|
||||
if (parent == this) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
this.parent = parent;
|
||||
addHandler(new FieldFilterDataAccessHandler())
|
||||
.addHandler(new DimensionDataAccessHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doAccess(DataAccessConfig access, AuthorizingContext context) {
|
||||
if (parent != null) {
|
||||
parent.doAccess(access, context);
|
||||
}
|
||||
return handlers.stream()
|
||||
.filter(handler -> handler.isSupport(access))
|
||||
.allMatch(handler -> handler.handle(access, context));
|
||||
}
|
||||
|
||||
public DefaultDataAccessController addHandler(DataAccessHandler handler) {
|
||||
handlers.add(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setHandlers(List<DataAccessHandler> handlers) {
|
||||
this.handlers = handlers;
|
||||
}
|
||||
|
||||
public List<DataAccessHandler> getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.hswebframework.ezorm.core.param.Param;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.web.api.crud.entity.Entity;
|
||||
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.Dimension;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.annotation.DimensionDataAccess;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.Phased;
|
||||
import org.hswebframework.web.authorization.exception.AccessDenyException;
|
||||
import org.hswebframework.web.authorization.simple.DimensionDataAccessConfig;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Slf4j
|
||||
public class DimensionDataAccessHandler implements DataAccessHandler {
|
||||
@Override
|
||||
public boolean isSupport(DataAccessConfig access) {
|
||||
return access instanceof DimensionDataAccessConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(DataAccessConfig access, AuthorizingContext context) {
|
||||
DimensionDataAccessConfig config = ((DimensionDataAccessConfig) access);
|
||||
DataAccessHandlerContext requestContext = DataAccessHandlerContext.of(context, config.getScopeType());
|
||||
if (!checkSupported(config, requestContext)) {
|
||||
return false;
|
||||
}
|
||||
switch (access.getAction()) {
|
||||
case Permission.ACTION_QUERY:
|
||||
case Permission.ACTION_GET:
|
||||
return doHandleQuery(config, requestContext);
|
||||
case Permission.ACTION_ADD:
|
||||
case Permission.ACTION_SAVE:
|
||||
case Permission.ACTION_UPDATE:
|
||||
return doHandleUpdate(config, requestContext);
|
||||
case Permission.ACTION_DELETE:
|
||||
return doHandleDelete(config, requestContext);
|
||||
default:
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("data access [{}] not support for {}", config.getType().getId(), access.getAction());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected String getProperty(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext ct) {
|
||||
return Optional.ofNullable(
|
||||
getMappingInfo(ct).get(cfg.getScopeType()))
|
||||
.map(MappingInfo::getProperty)
|
||||
.orElseGet(() -> {
|
||||
log.warn("{} not supported dimension data access", ct.getParamContext().getMethod());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean checkSupported(DimensionDataAccessConfig cfg, DataAccessHandlerContext ctx) {
|
||||
Authentication authentication = ctx.getAuthentication();
|
||||
|
||||
/*
|
||||
DataAccessHelper.assert()
|
||||
*/
|
||||
if (CollectionUtils.isEmpty(ctx.getDimensions())) {
|
||||
log.warn("user:[{}] dimension not setup", authentication.getUser().getId());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getMappingInfo(ctx).containsKey(cfg.getScopeType())) {
|
||||
log.warn("{} not supported dimension data access.see annotation: @DimensionDataAccess", ctx.getParamContext().getMethod());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean doHandleDelete(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext context) {
|
||||
|
||||
|
||||
// TODO: 2019-11-18
|
||||
return doHandleUpdate(cfg, context);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected Object handleById(DimensionDataAccessConfig config,
|
||||
DataAccessHandlerContext context,
|
||||
MappingInfo mappingInfo,
|
||||
Object id) {
|
||||
|
||||
if (id instanceof Param || id instanceof Entity) {
|
||||
|
||||
applyQueryParam(config, context, id);
|
||||
return id;
|
||||
}
|
||||
|
||||
List<Dimension> dimensions = context.getDimensions();
|
||||
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(config.getScope()) ?
|
||||
config.getScope() :
|
||||
dimensions
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
Function<Collection<Object>, Mono<Void>> reactiveCheck = obj -> context
|
||||
.getRepository()
|
||||
.findById(obj)
|
||||
.doOnNext(r -> {
|
||||
Object val = FastBeanCopier.copy(r, new HashMap<>(), FastBeanCopier.include(mappingInfo.getProperty()))
|
||||
.get(mappingInfo.getProperty());
|
||||
if (!StringUtils.isEmpty(val)
|
||||
&& !scope.contains(val)) {
|
||||
throw new AccessDenyException();
|
||||
}
|
||||
})
|
||||
.then();
|
||||
if (id instanceof Publisher) {
|
||||
if (id instanceof Mono) {
|
||||
return ((Mono) id)
|
||||
.flatMap(r -> {
|
||||
if (r instanceof Param) {
|
||||
applyQueryParam(config, context, r);
|
||||
return Mono.just(r);
|
||||
}
|
||||
return reactiveCheck.apply(r instanceof Collection ? ((Collection) r) : Collections.singleton(r));
|
||||
|
||||
})
|
||||
.then((Mono) id);
|
||||
}
|
||||
if (id instanceof Flux) {
|
||||
return ((Flux) id)
|
||||
.filter(v -> {
|
||||
if (v instanceof Param) {
|
||||
applyQueryParam(config, context, v);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.collectList()
|
||||
.flatMap(reactiveCheck)
|
||||
.thenMany((Flux) id);
|
||||
}
|
||||
}
|
||||
Collection<Object> idVal = id instanceof Collection ? ((Collection) id) : Collections.singleton(id);
|
||||
|
||||
Object result = context.getParamContext().getInvokeResult();
|
||||
if (result instanceof Mono) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(reactiveCheck.apply(idVal).then(((Mono) result)));
|
||||
|
||||
} else if (result instanceof Flux) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(reactiveCheck.apply(idVal).thenMany(((Flux) result)));
|
||||
} else {
|
||||
// TODO: 2019-11-19 非响应式处理
|
||||
log.warn("unsupported handle data access by id :{}", context.getParamContext().getMethod());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
protected boolean doHandleUpdate(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext context) {
|
||||
MappingInfo info = getMappingInfo(context).get(cfg.getScopeType());
|
||||
if (info != null) {
|
||||
if (info.idParamIndex != -1) {
|
||||
Object param = context.getParamContext().getArguments()[info.idParamIndex];
|
||||
context.getParamContext().getArguments()[info.idParamIndex] = handleById(cfg, context, info, param);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean reactive = context.getParamContext()
|
||||
.handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono.from(publisher)
|
||||
.flatMap(payload -> applyReactiveUpdatePayload(cfg, info, Collections.singleton(payload), context)
|
||||
.thenReturn(payload));
|
||||
}
|
||||
if (publisher instanceof Flux) {
|
||||
return Flux.from(publisher)
|
||||
.collectList()
|
||||
.flatMapMany(list ->
|
||||
applyReactiveUpdatePayload(cfg, info, list, context)
|
||||
.flatMapIterable(v -> list));
|
||||
}
|
||||
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (!reactive) {
|
||||
applyUpdatePayload(cfg, info, Arrays
|
||||
.stream(context.getParamContext().getArguments())
|
||||
.flatMap(obj -> {
|
||||
if (obj instanceof Collection) {
|
||||
return ((Collection<?>) obj).stream();
|
||||
}
|
||||
return Stream.of(obj);
|
||||
})
|
||||
.filter(Entity.class::isInstance)
|
||||
.collect(Collectors.toSet()), context);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected void applyUpdatePayload(DimensionDataAccessConfig config,
|
||||
MappingInfo mappingInfo,
|
||||
Collection<?> payloads,
|
||||
DataAccessHandlerContext context) {
|
||||
List<Dimension> dimensions = context.getDimensions();
|
||||
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(config.getScope()) ?
|
||||
config.getScope() :
|
||||
dimensions
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (Object payload : payloads) {
|
||||
if (!(payload instanceof Entity)) {
|
||||
continue;
|
||||
}
|
||||
if (payload instanceof Param) {
|
||||
applyQueryParam(config, context, ((Param) payload));
|
||||
continue;
|
||||
}
|
||||
String property = mappingInfo.getProperty();
|
||||
Map<String, Object> map = FastBeanCopier.copy(payload, new HashMap<>(), FastBeanCopier.include(property));
|
||||
Object value = map.get(property);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
if (dimensions.size() == 1) {
|
||||
map.put(property, dimensions.get(0).getId());
|
||||
FastBeanCopier.copy(map, payload, property);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(scope)) {
|
||||
if (!scope.contains(value)) {
|
||||
throw new AccessDenyException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Mono<Void> applyReactiveUpdatePayload(DimensionDataAccessConfig config,
|
||||
MappingInfo info,
|
||||
Collection<?> payloads,
|
||||
DataAccessHandlerContext context) {
|
||||
|
||||
return Mono.fromRunnable(() -> applyUpdatePayload(config, info, payloads, context));
|
||||
}
|
||||
|
||||
protected boolean hasAccessByProperty(Set<Object> scope, String property, Object payload) {
|
||||
Map<String, Object> values = FastBeanCopier.copy(payload, new HashMap<>(), FastBeanCopier.include(property));
|
||||
Object val = values.get(property);
|
||||
return val == null || scope.contains(val);
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected boolean doHandleQuery(DimensionDataAccessConfig cfg, DataAccessHandlerContext context) {
|
||||
MappingInfo mappingInfo = getMappingInfo(context).get(cfg.getScopeType());
|
||||
|
||||
//根据结果控制
|
||||
if (context.getDefinition().getResources().getPhased() == Phased.after) {
|
||||
Object result = context.getParamContext().getInvokeResult();
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(cfg.getScope()) ?
|
||||
cfg.getScope() :
|
||||
context.getDimensions()
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
String property = mappingInfo.getProperty();
|
||||
|
||||
if (result instanceof Mono) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(((Mono) result).
|
||||
filter(data -> hasAccessByProperty(scope, property, data)));
|
||||
return true;
|
||||
} else if (result instanceof Flux) {
|
||||
context.getParamContext()
|
||||
.setInvokeResult(((Flux) result).
|
||||
filter(data -> hasAccessByProperty(scope, property, data)));
|
||||
return true;
|
||||
}
|
||||
return hasAccessByProperty(scope, property, result);
|
||||
}
|
||||
//根据id控制
|
||||
if (mappingInfo.getIdParamIndex() >= 0) {
|
||||
Object param = context.getParamContext().getArguments()[mappingInfo.idParamIndex];
|
||||
context.getParamContext().getArguments()[mappingInfo.idParamIndex] = handleById(cfg, context, mappingInfo, param);
|
||||
return true;
|
||||
}
|
||||
|
||||
//根据查询条件控制
|
||||
boolean reactive = context.getParamContext().handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono
|
||||
.from(publisher)
|
||||
.flatMap(param -> this
|
||||
.applyReactiveQueryParam(cfg, context, param)
|
||||
.thenReturn(param));
|
||||
}
|
||||
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (!reactive) {
|
||||
Object[] args = context.getParamContext().getArguments();
|
||||
this.applyQueryParam(cfg, context, args);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected String getTermType(DimensionDataAccessConfig cfg) {
|
||||
return "in";
|
||||
}
|
||||
|
||||
protected void applyQueryParam(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext requestContext,
|
||||
Param param) {
|
||||
Set<Object> scope = CollectionUtils.isNotEmpty(cfg.getScope()) ?
|
||||
cfg.getScope() :
|
||||
requestContext.getDimensions()
|
||||
.stream()
|
||||
.map(Dimension::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
QueryParamEntity entity = new QueryParamEntity();
|
||||
entity.setTerms(new ArrayList<>(param.getTerms()));
|
||||
entity.toNestQuery(query ->
|
||||
query.where(
|
||||
getProperty(cfg, requestContext),
|
||||
getTermType(cfg),
|
||||
scope));
|
||||
param.setTerms(entity.getTerms());
|
||||
}
|
||||
|
||||
protected void applyQueryParam(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext requestContext,
|
||||
Object... params) {
|
||||
for (Object param : params) {
|
||||
if (param instanceof QueryParam) {
|
||||
applyQueryParam(cfg, requestContext, (QueryParam) param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Mono<Void> applyReactiveQueryParam(DimensionDataAccessConfig cfg,
|
||||
DataAccessHandlerContext requestContext,
|
||||
Object... param) {
|
||||
|
||||
|
||||
return Mono.fromRunnable(() -> applyQueryParam(cfg, requestContext, param));
|
||||
}
|
||||
|
||||
private Map<Method, Map<String, MappingInfo>> cache = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
public Map<String, MappingInfo> getMappingInfo(DataAccessHandlerContext context) {
|
||||
return getMappingInfo(ClassUtils.getUserClass(context.getParamContext().getTarget()), context.getParamContext().getMethod());
|
||||
|
||||
}
|
||||
|
||||
private Set<Class<? extends Annotation>> ann = new HashSet<>(Arrays.asList(DimensionDataAccess.class, DimensionDataAccess.Mapping.class));
|
||||
|
||||
|
||||
private Map<String, MappingInfo> getMappingInfo(Class target, Method method) {
|
||||
|
||||
return cache.computeIfAbsent(method, m -> {
|
||||
Set<Annotation> methodAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(method, ann);
|
||||
Set<Annotation> classAnnotation = AnnotatedElementUtils.findAllMergedAnnotations(target, ann);
|
||||
|
||||
|
||||
List<Annotation> all = new ArrayList<>(classAnnotation);
|
||||
all.addAll(methodAnnotation);
|
||||
if (CollectionUtils.isEmpty(all)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, MappingInfo> mappingInfoMap = new HashMap<>();
|
||||
for (Annotation annotation : all) {
|
||||
if (annotation instanceof DimensionDataAccess) {
|
||||
for (DimensionDataAccess.Mapping mapping : ((DimensionDataAccess) annotation).mapping()) {
|
||||
mappingInfoMap.put(mapping.dimensionType(), MappingInfo.of(mapping));
|
||||
}
|
||||
}
|
||||
if (annotation instanceof DimensionDataAccess.Mapping) {
|
||||
mappingInfoMap.put(((DimensionDataAccess.Mapping) annotation).dimensionType(), MappingInfo.of(((DimensionDataAccess.Mapping) annotation)));
|
||||
}
|
||||
}
|
||||
return mappingInfoMap;
|
||||
});
|
||||
}
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
static class MappingInfo {
|
||||
String dimension;
|
||||
|
||||
String property;
|
||||
|
||||
int idParamIndex;
|
||||
|
||||
static MappingInfo of(DimensionDataAccess.Mapping mapping) {
|
||||
return new MappingInfo(mapping.dimensionType(), mapping.property(), mapping.idParamIndex());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import org.apache.commons.beanutils.BeanUtilsBean;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.web.authorization.Permission;
|
||||
import org.hswebframework.web.authorization.access.DataAccessConfig;
|
||||
import org.hswebframework.web.authorization.access.DataAccessHandler;
|
||||
import org.hswebframework.web.authorization.access.FieldFilterDataAccessConfig;
|
||||
import org.hswebframework.web.authorization.define.AuthorizingContext;
|
||||
import org.hswebframework.web.authorization.define.Phased;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 数据权限字段过滤处理,目前仅支持deny. {@link DataAccessConfig.DefaultType#DENY_FIELDS}
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class FieldFilterDataAccessHandler implements DataAccessHandler {
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@Override
|
||||
public boolean isSupport(DataAccessConfig access) {
|
||||
return access instanceof FieldFilterDataAccessConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(DataAccessConfig access, AuthorizingContext context) {
|
||||
FieldFilterDataAccessConfig filterDataAccessConfig = ((FieldFilterDataAccessConfig) access);
|
||||
|
||||
switch (access.getAction()) {
|
||||
case Permission.ACTION_QUERY:
|
||||
case Permission.ACTION_GET:
|
||||
return doQueryAccess(filterDataAccessConfig, context);
|
||||
case Permission.ACTION_ADD:
|
||||
case Permission.ACTION_SAVE:
|
||||
case Permission.ACTION_UPDATE:
|
||||
return doUpdateAccess(filterDataAccessConfig, context);
|
||||
default:
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("field filter not support for {}", access.getAction());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void applyUpdateParam(FieldFilterDataAccessConfig config, Object... parameter) {
|
||||
|
||||
for (Object data : parameter) {
|
||||
for (String field : config.getFields()) {
|
||||
try {
|
||||
//设置值为null,跳过修改
|
||||
BeanUtilsBean.getInstance()
|
||||
.getPropertyUtils()
|
||||
.setProperty(data, field, null);
|
||||
} catch (Exception e) {
|
||||
logger.warn("can't set {} null", field, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accesses 不可操作的字段
|
||||
* @param params 参数上下文
|
||||
* @return true
|
||||
* @see BeanUtilsBean
|
||||
* @see org.apache.commons.beanutils.PropertyUtilsBean
|
||||
*/
|
||||
protected boolean doUpdateAccess(FieldFilterDataAccessConfig accesses, AuthorizingContext params) {
|
||||
|
||||
boolean reactive = params.getParamContext().handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono.from(publisher)
|
||||
.doOnNext(data -> applyUpdateParam(accesses, data));
|
||||
|
||||
}
|
||||
if (publisher instanceof Flux) {
|
||||
return Flux.from(publisher)
|
||||
.doOnNext(data -> applyUpdateParam(accesses, data));
|
||||
|
||||
}
|
||||
return publisher;
|
||||
});
|
||||
if (reactive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
applyUpdateParam(accesses, params.getParamContext().getArguments());
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected void applyQueryParam(FieldFilterDataAccessConfig config, Object param) {
|
||||
if (param instanceof QueryParam) {
|
||||
Set<String> denyFields = config.getFields();
|
||||
((QueryParam) param).excludes(denyFields.toArray(new String[0]));
|
||||
return;
|
||||
}
|
||||
|
||||
Object r = InvokeResultUtils.convertRealResult(param);
|
||||
if (r instanceof Collection) {
|
||||
((Collection) r).forEach(o -> setObjectPropertyNull(o, config.getFields()));
|
||||
} else {
|
||||
setObjectPropertyNull(r, config.getFields());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
protected boolean doQueryAccess(FieldFilterDataAccessConfig access, AuthorizingContext context) {
|
||||
if (context.getDefinition().getResources().getPhased() == Phased.before) {
|
||||
|
||||
boolean reactive = context
|
||||
.getParamContext()
|
||||
.handleReactiveArguments(publisher -> {
|
||||
if (publisher instanceof Mono) {
|
||||
return Mono.from(publisher)
|
||||
.doOnNext(param -> {
|
||||
applyQueryParam(access, param);
|
||||
});
|
||||
}
|
||||
return publisher;
|
||||
});
|
||||
|
||||
if (reactive) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (Object argument : context.getParamContext().getArguments()) {
|
||||
applyQueryParam(access, argument);
|
||||
}
|
||||
} else {
|
||||
if (context.getParamContext().getInvokeResult() instanceof Publisher) {
|
||||
context.getParamContext().setInvokeResult(
|
||||
Flux.from((Publisher<?>) context.getParamContext().getInvokeResult())
|
||||
.doOnNext(result -> {
|
||||
applyQueryParam(access, result);
|
||||
})
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
applyQueryParam(access, context.getParamContext().getInvokeResult());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void setObjectPropertyNull(Object obj, Set<String> fields) {
|
||||
if (null == obj) {
|
||||
return;
|
||||
}
|
||||
for (String field : fields) {
|
||||
try {
|
||||
BeanUtilsBean.getInstance().getPropertyUtils().setProperty(obj, field, null);
|
||||
} catch (Exception ignore) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.handler.access;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
public class InvokeResultUtils {
|
||||
public static Object convertRealResult(Object result) {
|
||||
if (result instanceof ResponseEntity) {
|
||||
result = ((ResponseEntity) result).getBody();
|
||||
}
|
||||
// if (result instanceof ResponseMessage) {
|
||||
// result = ((ResponseMessage) result).getResult();
|
||||
// }
|
||||
// if (result instanceof PagerResult) {
|
||||
// result = ((PagerResult) result).getData();
|
||||
// }
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.hswebframework.web.authorization.basic.twofactor;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.authorization.User;
|
||||
import org.hswebframework.web.authorization.annotation.TwoFactor;
|
||||
import org.hswebframework.web.authorization.exception.NeedTwoFactorException;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidator;
|
||||
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @since 3.0.4
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class TwoFactorHandlerInterceptorAdapter extends HandlerInterceptorAdapter {
|
||||
|
||||
private final TwoFactorValidatorManager validatorManager;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
if (handler instanceof HandlerMethod) {
|
||||
HandlerMethod method = ((HandlerMethod) handler);
|
||||
TwoFactor factor = method.getMethodAnnotation(TwoFactor.class);
|
||||
if (factor == null || factor.ignore()) {
|
||||
return true;
|
||||
}
|
||||
String userId = Authentication.current()
|
||||
.map(Authentication::getUser)
|
||||
.map(User::getId)
|
||||
.orElse(null);
|
||||
TwoFactorValidator validator = validatorManager.getValidator(userId, factor.value(), factor.provider());
|
||||
if (!validator.expired()) {
|
||||
return true;
|
||||
}
|
||||
String code = request.getParameter(factor.parameter());
|
||||
if (code == null) {
|
||||
code = request.getHeader(factor.parameter());
|
||||
}
|
||||
if (StringUtils.isEmpty(code)) {
|
||||
throw new NeedTwoFactorException("validation.need_two_factor_verify", factor.provider());
|
||||
} else if (!validator.verify(code, factor.timeout())) {
|
||||
throw new NeedTwoFactorException(factor.message(), factor.provider());
|
||||
}
|
||||
}
|
||||
return super.preHandle(request, response, handler);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.hswebframework.web.authorization.token.ParsedToken;
|
||||
import org.hswebframework.web.id.IDGenerator;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.hswebframework.web.authorization.basic.web;
|
||||
import org.hswebframework.web.authorization.Authentication;
|
||||
import org.hswebframework.web.utils.WebUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -5,8 +5,8 @@ import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import static org.hswebframework.web.authorization.basic.web.UserTokenGenerator.TOKEN_TYPE_SESSION_ID;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.hswebframework.web.authorization.basic.web;
|
||||
|
||||
import org.hswebframework.web.authorization.token.ParsedToken;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 令牌解析器,用于在接受到请求到时候,从请求中获取令牌
|
||||
|
||||
@@ -7,10 +7,11 @@ import org.hswebframework.web.authorization.token.UserToken;
|
||||
import org.hswebframework.web.authorization.token.UserTokenHolder;
|
||||
import org.hswebframework.web.authorization.token.UserTokenManager;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -20,7 +21,7 @@ import java.util.stream.Collectors;
|
||||
*
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class WebUserTokenInterceptor extends HandlerInterceptorAdapter {
|
||||
public class WebUserTokenInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final UserTokenManager userTokenManager;
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
module hsweb.authorization.basic {
|
||||
requires spring.core;
|
||||
requires hsweb.core;
|
||||
requires hsweb.authorization.api;
|
||||
requires hsweb.access.logging.api;
|
||||
requires spring.beans;
|
||||
requires spring.boot.autoconfigure;
|
||||
requires spring.context;
|
||||
requires spring.boot;
|
||||
requires reactor.core;
|
||||
requires static lombok;
|
||||
requires fastjson;
|
||||
requires commons.collections;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires jakarta.annotation;
|
||||
requires org.slf4j;
|
||||
requires spring.aop;
|
||||
requires org.reactivestreams;
|
||||
requires spring.web;
|
||||
requires org.apache.commons.collections4;
|
||||
requires jakarta.validation;
|
||||
requires io.swagger.v3.oas.annotations;
|
||||
requires spring.webmvc;
|
||||
requires jakarta.servlet;
|
||||
requires org.apache.commons.codec;
|
||||
|
||||
exports org.hswebframework.web.authorization.basic.web;
|
||||
exports org.hswebframework.web.authorization.basic.aop;
|
||||
exports org.hswebframework.web.authorization.basic.configuration;
|
||||
exports org.hswebframework.web.authorization.basic.define;
|
||||
|
||||
opens org.hswebframework.web.authorization.basic.aop;
|
||||
opens org.hswebframework.web.authorization.basic.configuration;
|
||||
}
|
||||
@@ -61,113 +61,4 @@ public class AopAuthorizingControllerTest {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFiledDeny() {
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
SimpleFieldFilterDataAccessConfig config = new SimpleFieldFilterDataAccessConfig();
|
||||
config.setAction("query");
|
||||
config.setFields(new HashSet<>(Arrays.asList("name")));
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").username("test").build());
|
||||
authentication.setPermissions(Arrays.asList(SimplePermission.builder()
|
||||
.actions(Collections.singleton("query"))
|
||||
.dataAccesses(Collections.singleton(config))
|
||||
.id("test").build()));
|
||||
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Authentication> get() {
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
});
|
||||
|
||||
testController.queryUser(new QueryParam())
|
||||
.map(Param::getExcludes)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(f -> f.contains("name"))
|
||||
.verifyComplete();
|
||||
|
||||
testController.queryUser(Mono.just(new QueryParam()))
|
||||
.map(Param::getExcludes)
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches(f -> f.contains("name"))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDimensionDataAccess() {
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
DimensionDataAccessConfig config = new DimensionDataAccessConfig();
|
||||
config.setAction("query");
|
||||
config.setScopeType("role");
|
||||
|
||||
DimensionDataAccessConfig config2 = new DimensionDataAccessConfig();
|
||||
config2.setAction("save");
|
||||
config2.setScopeType("role");
|
||||
ReactiveAuthenticationHolder.setSupplier(new ReactiveAuthenticationSupplier() {
|
||||
@Override
|
||||
public Mono<Authentication> get(String userId) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Authentication> get() {
|
||||
return Mono.just(authentication);
|
||||
}
|
||||
});
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").username("test").build());
|
||||
authentication.setPermissions(Arrays.asList(SimplePermission.builder()
|
||||
.actions(new HashSet<>(Arrays.asList("query", "save")))
|
||||
.dataAccesses(new HashSet<>(Arrays.asList(config, config2)))
|
||||
.id("test").build()));
|
||||
authentication.setDimensions(Collections.singletonList(Dimension.of("test", "test", DefaultDimensionType.role)));
|
||||
|
||||
testController.queryUserByDimension(Mono.just(new QueryParam()))
|
||||
.map(Param::getTerms)
|
||||
.flatMapIterable(Function.identity())
|
||||
.next()
|
||||
.map(Term::getValue)
|
||||
.map(CastUtil::<Collection<Object>>cast)
|
||||
.flatMapIterable(Function.identity())
|
||||
.next()
|
||||
.as(StepVerifier::create)
|
||||
.expectNextMatches("test"::equals)
|
||||
.verifyComplete();
|
||||
|
||||
TestEntity testEntity = new TestEntity();
|
||||
testEntity.setRoleId("123");
|
||||
|
||||
testController.save(Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectError(AccessDenyException.class)
|
||||
.verify();
|
||||
|
||||
testController.add(Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
testController.update(testEntity.getId(),Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectError(AccessDenyException.class)
|
||||
.verify();
|
||||
|
||||
testEntity = new TestEntity();
|
||||
testEntity.setRoleId("test");
|
||||
|
||||
testController.save(Mono.just(testEntity))
|
||||
.as(StepVerifier::create)
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import org.hswebframework.web.oauth2.ErrorType;
|
||||
import org.hswebframework.web.oauth2.OAuth2Exception;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.hswebframework.web.oauth2.server.impl;
|
||||
|
||||
import org.hswebframework.web.authorization.simple.SimpleAuthentication;
|
||||
import org.hswebframework.web.authorization.simple.SimpleUser;
|
||||
import org.hswebframework.web.oauth2.server.RedisHelper;
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -16,7 +17,7 @@ public class RedisAccessTokenManagerTest {
|
||||
RedisAccessTokenManager tokenManager = new RedisAccessTokenManager(RedisHelper.factory);
|
||||
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").build());
|
||||
tokenManager.createAccessToken("test", authentication, false)
|
||||
.doOnNext(System.out::println)
|
||||
.as(StepVerifier::create)
|
||||
@@ -30,7 +31,7 @@ public class RedisAccessTokenManagerTest {
|
||||
RedisAccessTokenManager tokenManager = new RedisAccessTokenManager(RedisHelper.factory);
|
||||
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").build());
|
||||
tokenManager
|
||||
.createAccessToken("test", authentication, false)
|
||||
.zipWhen(token -> tokenManager.refreshAccessToken("test", token.getRefreshToken()))
|
||||
@@ -47,7 +48,7 @@ public class RedisAccessTokenManagerTest {
|
||||
RedisAccessTokenManager tokenManager = new RedisAccessTokenManager(RedisHelper.factory);
|
||||
|
||||
SimpleAuthentication authentication = new SimpleAuthentication();
|
||||
|
||||
authentication.setUser(SimpleUser.builder().id("test").build());
|
||||
Flux
|
||||
.concat(tokenManager
|
||||
.createAccessToken("test", authentication, true),
|
||||
|
||||
@@ -38,11 +38,11 @@
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.google.code.findbugs</groupId>-->
|
||||
<!-- <artifactId>jsr305</artifactId>-->
|
||||
<!-- <scope>compile</scope>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.hswebframework.ezorm.core.param.TermType;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
package org.hswebframework.web.api.crud.entity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* 支持排序的实体
|
||||
|
||||
@@ -149,6 +149,25 @@
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>4.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-client-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.expressly</groupId>
|
||||
<artifactId>expressly</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -66,18 +66,11 @@ public class EasyormRepositoryRegistrar implements ImportBeanDefinitionRegistrar
|
||||
}
|
||||
|
||||
protected Set<String> scanEntities(String[] packageStr) {
|
||||
CandidateComponentsIndex index = CandidateComponentsIndexLoader.loadIndex(org.springframework.util.ClassUtils.getDefaultClassLoader());
|
||||
if (null == index) {
|
||||
return Stream
|
||||
.of(packageStr)
|
||||
.flatMap(this::doGetResources)
|
||||
.map(this::getResourceClassName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
return Stream
|
||||
.of(packageStr)
|
||||
.flatMap(pkg -> index.getCandidateTypes(pkg, Table.class.getName()).stream())
|
||||
.flatMap(this::doGetResources)
|
||||
.map(this::getResourceClassName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.hswebframework.web.crud.events.expr;
|
||||
|
||||
import io.netty.util.concurrent.FastThreadLocal;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.ezorm.rdb.mapping.EntityColumnMapping;
|
||||
import org.hswebframework.web.crud.query.QueryHelperUtils;
|
||||
@@ -13,7 +14,6 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import reactor.function.Function3;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.slf4j.Logger;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.springframework.transaction.reactive.TransactionSynchronization;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.hswebframework.web.datasource.DataSourceHolder;
|
||||
import org.hswebframework.web.datasource.R2dbcDataSource;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.r2dbc.connectionfactory.ConnectionFactoryUtils;
|
||||
import org.springframework.r2dbc.connection.ConnectionFactoryUtils;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.web.bind.support.WebExchangeBindException;
|
||||
import org.springframework.web.server.*;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -161,7 +161,7 @@ public class CommonErrorControllerAdvice {
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Mono<ResponseMessage<?>> handleException(javax.validation.ValidationException e) {
|
||||
public Mono<ResponseMessage<?>> handleException(jakarta.validation.ValidationException e) {
|
||||
return Mono.just(ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getMessage()));
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.springframework.web.server.ServerWebInputException;
|
||||
import org.springframework.web.server.UnsupportedMediaTypeStatusException;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import jakarta.validation.ConstraintViolationException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -135,7 +135,7 @@ public class CommonWebMvcErrorControllerAdvice {
|
||||
|
||||
@ExceptionHandler
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public ResponseMessage<?> handleException(javax.validation.ValidationException e) {
|
||||
public ResponseMessage<?> handleException(jakarta.validation.ValidationException e) {
|
||||
return ResponseMessage.error(400, CodeConstants.Error.illegal_argument, e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -13,7 +13,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
/**
|
||||
* 响应式保存接口,基于{@link ReactiveRepository}提供默认的新增,保存,修改接口.
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.hswebframework.web.crud;
|
||||
|
||||
import org.hswebframework.web.crud.entity.CustomTestEntity;
|
||||
import org.hswebframework.web.crud.entity.TestEntity;
|
||||
import org.hswebframework.web.crud.service.CustomTestCustom;
|
||||
import org.hswebframework.web.crud.service.TestEntityService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@@ -12,7 +13,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootTest(classes = {TestApplication.class,TestEntityService.class, CustomTestCustom.class})
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class CrudTests {
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package org.hswebframework.web.crud;
|
||||
|
||||
import org.hswebframework.web.api.crud.entity.EntityFactory;
|
||||
import org.hswebframework.web.crud.annotation.EnableEasyormRepository;
|
||||
import org.hswebframework.web.crud.entity.factory.EntityMappingCustomizer;
|
||||
import org.hswebframework.web.crud.entity.factory.MapperEntityFactory;
|
||||
import org.hswebframework.web.crud.events.TestEntityListener;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories;
|
||||
|
||||
@@ -20,4 +23,9 @@ public class TestApplication {
|
||||
customizers.forEach(customizer -> customizer.custom(factory));
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TestEntityListener testEntityListener(){
|
||||
return new TestEntityListener();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.hswebframework.web.validator.CreateGroup;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -20,7 +20,7 @@ import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
@@ -5,10 +5,9 @@ import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Component
|
||||
public class TestEntityListener {
|
||||
|
||||
AtomicInteger beforeCreate = new AtomicInteger();
|
||||
|
||||
@@ -8,9 +8,10 @@ import lombok.ToString;
|
||||
import org.hswebframework.ezorm.core.param.Sort;
|
||||
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
|
||||
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
|
||||
import org.hswebframework.web.crud.TestApplication;
|
||||
import org.hswebframework.web.crud.entity.EventTestEntity;
|
||||
import org.hswebframework.web.crud.entity.TestEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -19,16 +20,13 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.*;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootTest(classes = TestApplication.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
class DefaultQueryHelperTest {
|
||||
public class DefaultQueryHelperTest {
|
||||
|
||||
@Autowired
|
||||
private DatabaseOperator database;
|
||||
|
||||
@@ -4,7 +4,8 @@ import org.hswebframework.ezorm.rdb.executor.SqlRequest;
|
||||
import org.hswebframework.ezorm.rdb.executor.wrapper.ResultWrappers;
|
||||
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
|
||||
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.hswebframework.web.crud.TestApplication;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@@ -13,15 +14,15 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootTest(classes = TestApplication.class)
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
class QueryAnalyzerImplTest {
|
||||
public class QueryAnalyzerImplTest {
|
||||
@Autowired
|
||||
private DatabaseOperator database;
|
||||
|
||||
|
||||
@Test
|
||||
void testInject() {
|
||||
public void testInject() {
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
|
||||
"select count(distinct time) t2, \"name\" n from \"s_test\" t");
|
||||
SqlRequest request = analyzer.refactor(
|
||||
@@ -43,7 +44,7 @@ class QueryAnalyzerImplTest {
|
||||
|
||||
|
||||
@Test
|
||||
void testUnion() {
|
||||
public void testUnion() {
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
|
||||
"select name n from s_test t " +
|
||||
"union select name n from s_test t");
|
||||
@@ -56,7 +57,7 @@ class QueryAnalyzerImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
public void test() {
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
|
||||
"select name n from s_test t");
|
||||
|
||||
@@ -69,7 +70,7 @@ class QueryAnalyzerImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSub() {
|
||||
public void testSub() {
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(database,
|
||||
"select * from ( select distinct(name) as n from s_test ) t");
|
||||
|
||||
@@ -94,7 +95,7 @@ class QueryAnalyzerImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testJoin() {
|
||||
public void testJoin() {
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
|
||||
database,
|
||||
"select *,t2.c from s_test t " +
|
||||
@@ -110,7 +111,7 @@ class QueryAnalyzerImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPrepare(){
|
||||
public void testPrepare(){
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
|
||||
database,
|
||||
"select * from (select substring(id,9) id from s_test where left(id,1) = ?) t");
|
||||
@@ -122,7 +123,7 @@ class QueryAnalyzerImplTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWith(){
|
||||
public void testWith(){
|
||||
|
||||
QueryAnalyzerImpl analyzer = new QueryAnalyzerImpl(
|
||||
database,
|
||||
|
||||
@@ -14,7 +14,7 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@SpringBootTest(classes = TestApplication.class, args = "--hsweb.cache.type=guava")
|
||||
@SpringBootTest(classes ={ TestApplication.class,TestCacheEntityService.class}, args = "--hsweb.cache.type=guava")
|
||||
@RunWith(SpringRunner.class)
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
|
||||
public class GenericReactiveCacheSupportCrudServiceTest {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.hswebframework.web.crud.service;
|
||||
import org.hswebframework.ezorm.core.param.QueryParam;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
|
||||
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
|
||||
import org.hswebframework.web.crud.TestApplication;
|
||||
import org.hswebframework.web.crud.entity.TestTreeSortEntity;
|
||||
import org.hswebframework.web.exception.ValidationException;
|
||||
import org.junit.Test;
|
||||
@@ -19,7 +20,7 @@ import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@SpringBootTest
|
||||
@SpringBootTest(classes = {TestApplication.class,TestTreeSortEntityService.class})
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class ReactiveTreeSortEntityServiceTest {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<version>3.29.0-GA</version>
|
||||
<version>${javassist.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -58,8 +58,20 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.expressly</groupId>
|
||||
<artifactId>expressly</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.annotation</groupId>
|
||||
<artifactId>jakarta.annotation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
@@ -83,22 +95,19 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-expands-script</artifactId>
|
||||
<version>${hsweb.expands.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish</groupId>
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>jakarta.el</groupId>-->
|
||||
<!-- <artifactId>jakarta.el-api</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.google.code.findbugs</groupId>-->
|
||||
<!-- <artifactId>jsr305</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
@@ -132,5 +141,10 @@
|
||||
<version>0.1.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -11,7 +11,6 @@ import org.apache.commons.beanutils.PropertyUtilsBean;
|
||||
import org.hswebframework.utils.time.DateFormatter;
|
||||
import org.hswebframework.web.dict.EnumDict;
|
||||
import org.hswebframework.web.proxy.Proxy;
|
||||
import org.jctools.maps.NonBlockingHashMap;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.hswebframework.web.exception;
|
||||
|
||||
import org.hswebframework.web.i18n.LocaleUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.StringUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.hswebframework.web.exception;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -10,7 +11,6 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import reactor.core.CoreSubscriber;
|
||||
import reactor.core.publisher.*;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@@ -7,7 +7,6 @@ import javassist.bytecode.annotation.*;
|
||||
import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import sun.misc.CompoundEnumeration;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
@@ -76,11 +75,28 @@ public class Proxy<I> extends URLClassLoader {
|
||||
@SuppressWarnings("all")
|
||||
Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[loaders.size()];
|
||||
|
||||
for (int i = 0; i < loaders.size(); i++) {
|
||||
tmp[i] = loaders.get(i).getResources(name);
|
||||
}
|
||||
return new Enumeration<URL>() {
|
||||
|
||||
return new CompoundEnumeration<>(tmp);
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
for (Enumeration<URL> urlEnumeration : tmp) {
|
||||
if (urlEnumeration.hasMoreElements()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL nextElement() {
|
||||
for (Enumeration<URL> urlEnumeration : tmp) {
|
||||
if (urlEnumeration.hasMoreElements()) {
|
||||
return urlEnumeration.nextElement();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@@ -212,13 +228,15 @@ public class Proxy<I> extends URLClassLoader {
|
||||
|
||||
@SneakyThrows
|
||||
public I newInstance() {
|
||||
return getTargetClass().newInstance();
|
||||
return getTargetClass().getConstructor().newInstance();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@SuppressWarnings("all")
|
||||
public Class<I> getTargetClass() {
|
||||
if (targetClass == null) {
|
||||
targetClass = (Class) ctClass.toClass(this, null);
|
||||
byte[] code = ctClass.toBytecode();
|
||||
targetClass = (Class) defineClass(null, code, 0, code.length);
|
||||
}
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
@@ -2,15 +2,10 @@ package org.hswebframework.web.utils;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.BeanUtilsBean2;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
|
||||
import org.hswebframework.expands.script.engine.ExecuteResult;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -20,6 +15,7 @@ import java.util.regex.Pattern;
|
||||
* @since 3.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Deprecated
|
||||
public class ExpressionUtils {
|
||||
|
||||
//表达式提取正则 ${.+?}
|
||||
@@ -81,10 +77,7 @@ public class ExpressionUtils {
|
||||
if (!expression.contains("${")) {
|
||||
return expression;
|
||||
}
|
||||
DynamicScriptEngine engine = DynamicScriptEngineFactory.getEngine(language);
|
||||
if (engine == null) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
|
||||
return TemplateParser.parse(expression, var -> {
|
||||
if (StringUtils.isEmpty(var)) {
|
||||
@@ -94,33 +87,7 @@ public class ExpressionUtils {
|
||||
if (val != null) {
|
||||
return String.valueOf(val);
|
||||
}
|
||||
if ("spel".equalsIgnoreCase(language) && !var.contains("#")) {
|
||||
try {
|
||||
Object fast = BeanUtilsBean2.getInstance().getPropertyUtils().getProperty(vars, var);
|
||||
if (fast != null) {
|
||||
return fast.toString();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
//ignore
|
||||
return "";
|
||||
}
|
||||
}
|
||||
String id = DigestUtils.md5Hex(var);
|
||||
try {
|
||||
if (!engine.compiled(id)) {
|
||||
engine.compile(id, var);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try {
|
||||
return String.valueOf(engine.execute(id, vars).getIfSuccess());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getLocalizedMessage(), e);
|
||||
return "";
|
||||
}
|
||||
return "";
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
|
||||
package org.hswebframework.web.utils;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Enumeration;
|
||||
|
||||
@@ -4,7 +4,7 @@ import org.hibernate.validator.BaseHibernateValidatorConfiguration;
|
||||
import org.hswebframework.web.exception.ValidationException;
|
||||
import org.hswebframework.web.i18n.ContextLocaleResolver;
|
||||
|
||||
import javax.validation.*;
|
||||
import jakarta.validation.*;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ValidatorUtils {
|
||||
@@ -28,7 +28,6 @@ public final class ValidatorUtils {
|
||||
configuration.messageInterpolator(configuration.getDefaultMessageInterpolator());
|
||||
|
||||
ValidatorFactory factory = configuration.buildValidatorFactory();
|
||||
|
||||
return validator = factory.getValidator();
|
||||
}
|
||||
}
|
||||
|
||||
54
hsweb-core/src/main/java9/module-info.java
Normal file
54
hsweb-core/src/main/java9/module-info.java
Normal file
@@ -0,0 +1,54 @@
|
||||
module hsweb.core{
|
||||
requires org.hibernate.validator;
|
||||
requires org.reactivestreams;
|
||||
requires hsweb.utils;
|
||||
requires com.google.common;
|
||||
requires reactor.core;
|
||||
requires fastjson;
|
||||
requires spring.beans;
|
||||
requires spring.core;
|
||||
requires java.desktop;
|
||||
requires commons.beanutils;
|
||||
requires jctools.core;
|
||||
requires org.aspectj.weaver;
|
||||
requires reactor.extra;
|
||||
requires io.netty.common;
|
||||
requires io.seruco.encoding.base62;
|
||||
requires spring.web;
|
||||
requires jakarta.servlet;
|
||||
requires jakarta.validation;
|
||||
requires spring.context;
|
||||
requires io.swagger.v3.oas.annotations;
|
||||
requires org.slf4j;
|
||||
requires com.fasterxml.jackson.core;
|
||||
requires com.fasterxml.jackson.annotation;
|
||||
requires com.fasterxml.jackson.databind;
|
||||
requires spring.aop;
|
||||
requires static lombok;
|
||||
requires jsr305;
|
||||
requires jakarta.annotation;
|
||||
requires org.javassist;
|
||||
requires java.base;
|
||||
requires org.apache.commons.codec;
|
||||
|
||||
|
||||
exports org.hswebframework.web;
|
||||
exports org.hswebframework.web.aop;
|
||||
exports org.hswebframework.web.bean;
|
||||
exports org.hswebframework.web.context;
|
||||
exports org.hswebframework.web.convert;
|
||||
exports org.hswebframework.web.dict;
|
||||
exports org.hswebframework.web.dict.defaults;
|
||||
exports org.hswebframework.web.enums;
|
||||
exports org.hswebframework.web.event;
|
||||
exports org.hswebframework.web.exception;
|
||||
exports org.hswebframework.web.i18n;
|
||||
exports org.hswebframework.web.id;
|
||||
exports org.hswebframework.web.logger;
|
||||
exports org.hswebframework.web.proxy;
|
||||
exports org.hswebframework.web.utils;
|
||||
exports org.hswebframework.web.validator;
|
||||
|
||||
opens org.hswebframework.web.validator;
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -127,11 +128,12 @@ public class FastBeanCopierTest {
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void testCrossClassLoader() {
|
||||
URL clazz = Source.class.getResource("/");
|
||||
URL clazz = new File("target/test-classes").getAbsoluteFile().toURI().toURL();
|
||||
|
||||
System.out.println(clazz);
|
||||
URLClassLoader loader = new URLClassLoader(new URL[]{
|
||||
clazz
|
||||
}, ClassLoader.getSystemClassLoader()){
|
||||
}, ClassUtils.getDefaultClassLoader()){
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
try {
|
||||
@@ -143,7 +145,7 @@ public class FastBeanCopierTest {
|
||||
return clazz;
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
// ignore.printStackTrace();
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
@@ -189,7 +191,7 @@ public class FastBeanCopierTest {
|
||||
public void testProxy() {
|
||||
AtomicReference<Object> reference = new AtomicReference<>();
|
||||
|
||||
ProxyTest test = (ProxyTest) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
|
||||
ProxyTest test = (ProxyTest) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(),
|
||||
new Class[]{ProxyTest.class}, (proxy, method, args) -> {
|
||||
if (method.getName().equals("getName")) {
|
||||
return "test";
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package org.hswebframework.web.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
public class ExpressionUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testArray() {
|
||||
String expression = ExpressionUtils.analytical("test-${array}", Collections.singletonMap("array", Arrays.asList(1,2,3)), "spel");
|
||||
|
||||
Assert.assertEquals(expression,"test-[1, 2, 3]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String expression = ExpressionUtils.analytical("test-${#name}", Collections.singletonMap("name", "test"), "spel");
|
||||
|
||||
Assert.assertEquals(expression,"test-test");
|
||||
|
||||
String res = ExpressionUtils.analytical("test-${3+2}", Collections.singletonMap("name", "test"), "spel");
|
||||
|
||||
Assert.assertEquals(res,"test-5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComplete(){
|
||||
TemplateParser.parse("${#data[payload][a_name]} ${#data[payload][b_name]} 发生 ${#data[payload][alarm_type_name]}",e->{
|
||||
System.out.println(e);
|
||||
return e;
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJson(){
|
||||
|
||||
Assert.assertEquals("{\"name\":\"test\"}",TemplateParser.parse("{\"name\":\"${#name}\"}",e->{
|
||||
System.out.println(e);
|
||||
Assert.assertEquals(e,"#name");
|
||||
return "test";
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJson2(){
|
||||
String js = ExpressionUtils.analytical("{\n" +
|
||||
" \"msgtype\": \"markdown\",\n" +
|
||||
" \"markdown\": {\n" +
|
||||
" \"title\":\"消息类型:${messageType}\",\n" +
|
||||
" \"text\": \" - 设备ID: `${deviceId}` 值:`${properties.a-r-str}` \\n - 设备型号: `${headers.productId}`\\n - 设备名称: `${headers.deviceName}`\"" +
|
||||
" \n},\n" +
|
||||
" \"at\": {\n" +
|
||||
" \"isAtAll\": false\n" +
|
||||
" }\n" +
|
||||
"}", JSON.parseObject("{\n" +
|
||||
" \"deviceId\": \"VIS-Mandrake-12289\",\n" +
|
||||
" \"headers\": {\n" +
|
||||
" \"productId\": \"VIS-Mandrake\",\n" +
|
||||
" \"deviceName\": \"能见度仪-曼德克-01\"\n" +
|
||||
" }, \"properties\": {\n" +
|
||||
" \"a-r-str\": \"a2\"\n" +
|
||||
" },\n" +
|
||||
" \"messageType\": \"OFFLINE\",\n" +
|
||||
" \"timestamp\": 1592098397277\n" +
|
||||
"}"), "spel");
|
||||
|
||||
System.out.println(js);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import org.hswebframework.web.i18n.MessageSourceInitializer;
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# 动态数据源JTA实现 (atomikos)
|
||||
使用atomikos实现动态数据源事务管理
|
||||
|
||||
# 数据源配置
|
||||
默认数据源配置,使用spring的jta配置即可:
|
||||
```yaml
|
||||
spring:
|
||||
jta:
|
||||
status: true
|
||||
atomikos:
|
||||
datasource:
|
||||
xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
|
||||
xa-properties:
|
||||
url : jdbc:h2:mem:core;DB_CLOSE_ON_EXIT=FALSE
|
||||
username : sa
|
||||
password :
|
||||
max-pool-size: 20
|
||||
borrow-connection-timeout: 1000
|
||||
connectionfactory:
|
||||
max-pool-size: 20
|
||||
local-transaction-mode: true
|
||||
```
|
||||
|
||||
动态数据源配置,默认提供一个 ``InMemoryAtomikosDataSourceRepository``,在application.yml 中进行配置即可:
|
||||
```yaml
|
||||
hsweb:
|
||||
datasource:
|
||||
jta:
|
||||
test_ds: # 数据源ID
|
||||
xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
|
||||
xa-properties: # 数据源的配置属性
|
||||
url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: sa
|
||||
password:
|
||||
max-pool-size: 20
|
||||
borrow-connection-timeout: 1000
|
||||
test_ds2: # 数据源ID
|
||||
xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
|
||||
xa-properties: # 数据源的配置属性
|
||||
url: jdbc:mysql://localhost:3306/hsweb?pinGlobalTxToPhysicalConnection=true&useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false
|
||||
# url: jdbc:h2:mem:test2;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: root
|
||||
password: "123456" # 纯数字密码要加上双引号,不然启动会报Cannot initialize AtomikosDataSourceBean
|
||||
max-pool-size: 20
|
||||
borrow-connection-timeout: 1000
|
||||
init-timeout: 20
|
||||
```
|
||||
|
||||
自定义,将数据源配置放到数据库中,实现 ``DynamicDataSourceConfigRepository<AtomikosDataSourceConfig>`` 接口并注入到spring容器即可
|
||||
@@ -1,85 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-datasource</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.17-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-datasource-jta</artifactId>
|
||||
|
||||
<description>基于atomikos的多数据源实现,支持事务中切换数据源</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid</artifactId>
|
||||
<version>1.0.26</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.jtds</groupId>
|
||||
<artifactId>jtds</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-activemq</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.29</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-easy-orm-rdb</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-datasource-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,39 +0,0 @@
|
||||
package org.hswebframework.web.datasource.jta;
|
||||
|
||||
import org.hswebframework.web.datasource.DynamicDataSourceAutoConfiguration;
|
||||
import org.hswebframework.web.datasource.DynamicDataSourceService;
|
||||
import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
@AutoConfiguration
|
||||
@AutoConfigureBefore(DynamicDataSourceAutoConfiguration.class)
|
||||
public class AtomikosDataSourceAutoConfiguration {
|
||||
|
||||
//默认数据源
|
||||
@Bean(initMethod = "init", destroyMethod = "destroy", value = "datasource")
|
||||
@Primary
|
||||
public AtomikosDataSourceBean datasource() {
|
||||
return new AtomikosDataSourceBean();
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean(DynamicDataSourceConfigRepository.class)
|
||||
@Bean
|
||||
public DynamicDataSourceConfigRepository inMemoryAtomikosDataSourceRepository() {
|
||||
return new InMemoryAtomikosDataSourceRepository();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package org.hswebframework.web.datasource.jta;
|
||||
|
||||
import com.atomikos.jdbc.AtomikosDataSourceBean;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.sql.XADataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Slf4j
|
||||
@Data
|
||||
public class AtomikosDataSourceConfig extends DynamicDataSourceConfig {
|
||||
private static final long serialVersionUID = 5588085000663972571L;
|
||||
private int minPoolSize = 5;
|
||||
private int maxPoolSize = 200;
|
||||
private int borrowConnectionTimeout = 60;
|
||||
private int reapTimeout = 0;
|
||||
private int maxIdleTime = 60;
|
||||
private int maintenanceInterval = 60;
|
||||
private int defaultIsolationLevel = -1;
|
||||
private String xaDataSourceClassName = null;
|
||||
private int loginTimeout = 0;
|
||||
private String testQuery = null;
|
||||
private int maxLifetime = 0;
|
||||
private Properties xaProperties = null;
|
||||
//初始化超时时间
|
||||
private int initTimeout = 10;
|
||||
|
||||
@SneakyThrows
|
||||
public void putProperties(AtomikosDataSourceBean atomikosDataSourceBean) {
|
||||
|
||||
if (null != xaProperties) {
|
||||
xaProperties.entrySet().forEach(entry -> entry.setValue(String.valueOf(entry.getValue())));
|
||||
}
|
||||
//fix #87
|
||||
XADataSource dataSource = (XADataSource) ClassUtils.forName(getXaDataSourceClassName(),null).newInstance();
|
||||
FastBeanCopier.copy(xaProperties, dataSource);
|
||||
atomikosDataSourceBean.setXaDataSource(dataSource);
|
||||
|
||||
atomikosDataSourceBean.setXaDataSourceClassName(getXaDataSourceClassName());
|
||||
atomikosDataSourceBean.setBorrowConnectionTimeout(getBorrowConnectionTimeout());
|
||||
if (loginTimeout != 0) {
|
||||
try {
|
||||
atomikosDataSourceBean.setLoginTimeout(getLoginTimeout());
|
||||
} catch (SQLException e) {
|
||||
log.warn(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
atomikosDataSourceBean.setMaxIdleTime(getMaxIdleTime());
|
||||
atomikosDataSourceBean.setMaxPoolSize(getMaxPoolSize());
|
||||
atomikosDataSourceBean.setMinPoolSize(getMinPoolSize());
|
||||
atomikosDataSourceBean.setDefaultIsolationLevel(getDefaultIsolationLevel());
|
||||
atomikosDataSourceBean.setMaintenanceInterval(getMaintenanceInterval());
|
||||
atomikosDataSourceBean.setReapTimeout(getReapTimeout());
|
||||
atomikosDataSourceBean.setTestQuery(getTestQuery());
|
||||
atomikosDataSourceBean.setXaProperties(getXaProperties());
|
||||
atomikosDataSourceBean.setMaxLifetime(getMaxLifetime());
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.hswebframework.web.datasource.jta;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "hsweb.datasource")
|
||||
public class InMemoryAtomikosDataSourceRepository implements JtaDataSourceRepository {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private Map<String, AtomikosDataSourceConfig> jta = new HashMap<>();
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
jta.forEach((id, config) -> {
|
||||
if (config.getId() == null) {
|
||||
config.setId(id);
|
||||
} else if (!config.getId().equals(id)) {
|
||||
jta.put(config.getId(), config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AtomikosDataSourceConfig> findAll() {
|
||||
return new ArrayList<>(jta.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomikosDataSourceConfig findById(String dataSourceId) {
|
||||
return jta.get(dataSourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomikosDataSourceConfig add(AtomikosDataSourceConfig config) {
|
||||
return jta.put(config.getId(), config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomikosDataSourceConfig remove(String dataSourceId) {
|
||||
return jta.remove(dataSourceId);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package org.hswebframework.web.datasource.jta;
|
||||
|
||||
import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
* @see org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository
|
||||
*/
|
||||
public interface JtaDataSourceRepository extends DynamicDataSourceConfigRepository<AtomikosDataSourceConfig> {
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
org.hswebframework.web.datasource.jta.AtomikosDataSourceAutoConfiguration
|
||||
@@ -1,63 +0,0 @@
|
||||
|
||||
spring:
|
||||
jta:
|
||||
status: true
|
||||
atomikos:
|
||||
datasource:
|
||||
xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
|
||||
xa-properties:
|
||||
url : jdbc:h2:mem:core;DB_CLOSE_ON_EXIT=FALSE
|
||||
username : sa
|
||||
password :
|
||||
maxActive: 1000
|
||||
initialSize: 5
|
||||
minIdle: 5
|
||||
maxWait: 50000
|
||||
max-pool-size: 20
|
||||
borrow-connection-timeout: 1000
|
||||
connectionfactory:
|
||||
max-pool-size: 20
|
||||
local-transaction-mode: true
|
||||
activemq:
|
||||
# broker-url: tcp://localhost:61616
|
||||
in-memory: true
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.atomikos: WARN
|
||||
org.hswebframework: DEBUG
|
||||
hsweb:
|
||||
datasource:
|
||||
jta:
|
||||
test_ds:
|
||||
xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
|
||||
xa-properties:
|
||||
url: jdbc:h2:mem:test;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: sa
|
||||
password:
|
||||
filters: stat
|
||||
maxActive: 1000
|
||||
initialSize: 5
|
||||
minIdle: 5
|
||||
maxWait: 50000
|
||||
max-pool-size: 20
|
||||
borrow-connection-timeout: 1000
|
||||
test_ds2:
|
||||
xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
|
||||
xa-properties:
|
||||
# url: jdbc:mysql://localhost:3306/hsweb?pinGlobalTxToPhysicalConnection=true&useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&failOverReadOnly=false
|
||||
url: jdbc:h2:mem:test2;DB_CLOSE_ON_EXIT=FALSE
|
||||
username: sa
|
||||
password:
|
||||
max-pool-size: 20
|
||||
borrow-connection-timeout: 1000
|
||||
init-timeout: 20
|
||||
switcher:
|
||||
test: # 只是一个标识
|
||||
# 拦截表达式
|
||||
expression: org.hswebframework.**.*DynDsTest.find*
|
||||
# 使用数据源
|
||||
data-source-id: test_ds
|
||||
|
||||
config:
|
||||
test: 123
|
||||
@@ -1,32 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hsweb-datasource</artifactId>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<version>4.0.17-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>hsweb-datasource-web</artifactId>
|
||||
|
||||
<description>暴露多数据源的web接口</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-datasource-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework.web</groupId>
|
||||
<artifactId>hsweb-authorization-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.hswebframework.web.datasource.web;
|
||||
|
||||
import org.hswebframework.web.authorization.annotation.QueryAction;
|
||||
import org.hswebframework.web.authorization.annotation.Resource;
|
||||
import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
|
||||
import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/datasource")
|
||||
@Resource(id = "datasource", name = "数据源管理")
|
||||
public class DatasourceController {
|
||||
|
||||
@Autowired
|
||||
private DynamicDataSourceConfigRepository<? extends DynamicDataSourceConfig> repository;
|
||||
|
||||
@GetMapping
|
||||
@QueryAction
|
||||
public List<? extends DynamicDataSourceConfig> getAllConfig() {
|
||||
return repository.findAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package org.hswebframework.web.datasource.web;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@AutoConfiguration
|
||||
public class DatasourceWebApiAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public DatasourceController datasourceController() {
|
||||
return new DatasourceController();
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
org.hswebframework.web.datasource.web.DatasourceWebApiAutoConfiguration
|
||||
@@ -17,8 +17,6 @@
|
||||
<packaging>pom</packaging>
|
||||
<modules>
|
||||
<module>hsweb-datasource-api</module>
|
||||
<module>hsweb-datasource-jta</module>
|
||||
<module>hsweb-datasource-web</module>
|
||||
</modules>
|
||||
|
||||
|
||||
|
||||
@@ -61,8 +61,8 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -28,7 +28,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.util.context.Context;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -212,7 +212,7 @@ public class ReactiveAopAccessLoggerSupport extends StaticMethodMatcherPointcutA
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
info.setRequestId(request.getId());
|
||||
info.setPath(request.getPath().value());
|
||||
info.setRequestMethod(request.getMethodValue());
|
||||
info.setRequestMethod(request.getMethod().name());
|
||||
info.setHeaders(request.getHeaders().toSingleValueMap());
|
||||
|
||||
Optional.ofNullable(ReactiveWebUtils.getIpAddr(request))
|
||||
|
||||
@@ -17,18 +17,6 @@
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hswebframework</groupId>
|
||||
<artifactId>hsweb-expands-script</artifactId>
|
||||
<version>${hsweb.expands.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
@@ -83,9 +71,28 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
<scope>compile</scope>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.websocket</groupId>
|
||||
<artifactId>jakarta.websocket-client-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.glassfish.expressly</groupId>
|
||||
<artifactId>expressly</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>com.google.code.findbugs</groupId>-->
|
||||
<!-- <artifactId>jsr305</artifactId>-->
|
||||
<!-- <scope>compile</scope>-->
|
||||
<!-- </dependency>-->
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -67,7 +67,7 @@ import java.util.Optional;
|
||||
@EnableConfigurationProperties(CorsProperties.class)
|
||||
public class CorsAutoConfiguration {
|
||||
|
||||
@ConditionalOnClass(name = "javax.servlet.Filter")
|
||||
@ConditionalOnClass(name = "jakarta.servlet.Filter")
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||
@Configuration
|
||||
static class WebMvcCorsConfiguration {
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package org.hswebframework.web.starter;
|
||||
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
|
||||
import org.hswebframework.ezorm.rdb.executor.SyncSqlExecutor;
|
||||
import org.hswebframework.ezorm.rdb.executor.reactive.ReactiveSqlExecutor;
|
||||
import org.hswebframework.ezorm.rdb.executor.reactive.ReactiveSyncSqlExecutor;
|
||||
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
|
||||
import org.hswebframework.web.starter.initialize.AppProperties;
|
||||
import org.hswebframework.web.starter.initialize.SystemInitialize;
|
||||
import org.hswebframework.web.starter.initialize.SystemVersion;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(AppProperties.class)
|
||||
public class HswebAutoConfiguration {
|
||||
|
||||
private List<DynamicScriptEngine> engines;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
engines = Stream.of("js", "groovy")
|
||||
.map(DynamicScriptEngineFactory::getEngine)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
addGlobalVariable("logger", LoggerFactory.getLogger("org.hswebframework.script"));
|
||||
|
||||
addGlobalVariable("spring", applicationContext);
|
||||
}
|
||||
|
||||
private void addGlobalVariable(String var, Object val) {
|
||||
engines.forEach(engine -> {
|
||||
try {
|
||||
engine.addGlobalVariable(Collections.singletonMap(var, val));
|
||||
} catch (NullPointerException ignore) {
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner systemInit(DatabaseOperator database,
|
||||
AppProperties properties) {
|
||||
|
||||
addGlobalVariable("database", database);
|
||||
addGlobalVariable("sqlExecutor", database
|
||||
.getMetadata()
|
||||
.getFeature(SyncSqlExecutor.ID)
|
||||
.orElseGet(() -> database
|
||||
.getMetadata()
|
||||
.getFeature(ReactiveSqlExecutor.ID)
|
||||
.map(ReactiveSyncSqlExecutor::of)
|
||||
.orElse(null)));
|
||||
SystemVersion version = properties.build();
|
||||
return args -> {
|
||||
if (properties.isAutoInit()) {
|
||||
SystemInitialize initialize = new SystemInitialize(database, version);
|
||||
initialize.setExcludeTables(properties.getInitTableExcludes());
|
||||
initialize.install();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import org.springframework.context.NoSuchMessageException;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ConfigurationProperties(prefix = "hsweb.app")
|
||||
@Getter
|
||||
@Setter
|
||||
public class AppProperties {
|
||||
private boolean autoInit = true;
|
||||
private List<String> initTableExcludes;
|
||||
|
||||
private String name = "default";
|
||||
private String comment;
|
||||
private String website;
|
||||
private String version;
|
||||
|
||||
public SystemVersion build() {
|
||||
SystemVersion systemVersion = new SystemVersion();
|
||||
systemVersion.setName(name);
|
||||
systemVersion.setComment(comment);
|
||||
systemVersion.setWebsite(website);
|
||||
systemVersion.setVersion(version);
|
||||
return systemVersion;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface CallBack {
|
||||
void execute(Map<String, Object> context);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class DefaultDependencyUpgrader implements DependencyUpgrader {
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
Dependency installed;
|
||||
Dependency dependency;
|
||||
List<Map<String, Object>> shouldUpdateVersionList;
|
||||
private Map<String, Object> context;
|
||||
private boolean firstInstall;
|
||||
|
||||
public DefaultDependencyUpgrader(Dependency installed, Dependency dependency, Map<String, Object> context) {
|
||||
this.firstInstall = installed == null;
|
||||
if (firstInstall) {
|
||||
this.installed = dependency;
|
||||
} else {
|
||||
this.installed = installed;
|
||||
}
|
||||
this.context = context;
|
||||
this.dependency = dependency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyUpgrader filter(List<Map<String, Object>> versions) {
|
||||
shouldUpdateVersionList = versions.stream()
|
||||
.filter(map -> {
|
||||
String ver = (String) map.get("version");
|
||||
if (null == ver) {
|
||||
return false;
|
||||
}
|
||||
//首次安装
|
||||
if (firstInstall) {
|
||||
return true;
|
||||
}
|
||||
//相同版本
|
||||
if (installed.compareTo(dependency) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return installed.compareTo(new SystemVersion(ver)) < 0;
|
||||
})
|
||||
.sorted(Comparator.comparing(m -> new SystemVersion((String) m.get("version"))))
|
||||
.collect(Collectors.toList());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upgrade(CallBack callBack) {
|
||||
shouldUpdateVersionList.forEach(context -> {
|
||||
if (this.context != null) {
|
||||
context.putAll(this.context);
|
||||
}
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("upgrade [{}/{}] to version:{} {}", dependency.getGroupId(), dependency.getArtifactId(), context.get("version"), dependency.getWebsite());
|
||||
}
|
||||
callBack.execute(context);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class Dependency extends Version {
|
||||
protected String groupId;
|
||||
protected String artifactId;
|
||||
protected String author;
|
||||
|
||||
public String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public void setGroupId(String groupId) {
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
public String getArtifactId() {
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
public void setArtifactId(String artifactId) {
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public static Dependency fromMap(Map<String, Object> map) {
|
||||
Dependency dependency = new Dependency();
|
||||
dependency.setGroupId((String) map.get("groupId"));
|
||||
dependency.setArtifactId((String) map.get("artifactId"));
|
||||
dependency.setName((String) map.getOrDefault(SystemVersion.Property.name, dependency.getArtifactId()));
|
||||
dependency.setVersion((String) map.get("version"));
|
||||
dependency.setWebsite((String) map.get(SystemVersion.Property.website));
|
||||
dependency.setAuthor((String) map.get("author"));
|
||||
return dependency;
|
||||
}
|
||||
|
||||
public boolean isSameDependency(Dependency dependency) {
|
||||
return isSameDependency(dependency.getGroupId(), dependency.getArtifactId());
|
||||
}
|
||||
|
||||
public boolean isSameDependency(String groupId, String artifactId) {
|
||||
return groupId.equals(this.getGroupId()) && artifactId.equals(this.getArtifactId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return JSON.toJSONString(this, SerializerFeature.PrettyFormat);
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2020 http://www.hswebframework.org
|
||||
* *
|
||||
* * 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 org.hswebframework.web.starter.initialize;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DependencyInstaller {
|
||||
DependencyInstaller setup(Dependency dependency);
|
||||
|
||||
default DependencyInstaller setup(Map<String, Object> mapDependency) {
|
||||
return setup(Dependency.fromMap(mapDependency));
|
||||
}
|
||||
|
||||
DependencyInstaller onInstall(CallBack callBack);
|
||||
|
||||
DependencyInstaller onUpgrade(CallBack callBack);
|
||||
|
||||
DependencyInstaller onUninstall(CallBack callBack);
|
||||
|
||||
DependencyInstaller onInitialize(CallBack callBack);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2020 http://www.hswebframework.org
|
||||
* *
|
||||
* * 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 org.hswebframework.web.starter.initialize;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public interface DependencyUpgrader {
|
||||
DependencyUpgrader filter(List<Map<String, Object>> versions);
|
||||
|
||||
void upgrade(CallBack context);
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class SimpleDependencyInstaller implements DependencyInstaller {
|
||||
Dependency dependency;
|
||||
CallBack installer;
|
||||
CallBack upgrader;
|
||||
CallBack unInstaller;
|
||||
CallBack initializer;
|
||||
private Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
public SimpleDependencyInstaller() {
|
||||
}
|
||||
|
||||
public Dependency getDependency() {
|
||||
return dependency;
|
||||
}
|
||||
|
||||
public void doInstall(Map<String, Object> context) {
|
||||
if (installer != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("install [{}/{}]", dependency.getGroupId(), dependency.getArtifactId());
|
||||
}
|
||||
installer.execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
public void doInitialize(Map<String, Object> context) {
|
||||
if (initializer != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("initialize [{}/{}]", dependency.getGroupId(), dependency.getArtifactId());
|
||||
}
|
||||
initializer.execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
public void doUnInstall(Map<String, Object> context) {
|
||||
if (unInstaller != null) {
|
||||
unInstaller.execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
public void doUpgrade(Map<String, Object> context, Dependency installed) {
|
||||
DefaultDependencyUpgrader defaultDependencyUpgrader =
|
||||
new DefaultDependencyUpgrader(installed, dependency, context);
|
||||
context.put("upgrader", defaultDependencyUpgrader);
|
||||
if (upgrader != null) {
|
||||
upgrader.execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyInstaller setup(Dependency dependency) {
|
||||
this.dependency = dependency;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyInstaller onInstall(CallBack callBack) {
|
||||
this.installer = callBack;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyInstaller onUpgrade(CallBack callBack) {
|
||||
this.upgrader = callBack;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyInstaller onUninstall(CallBack callBack) {
|
||||
this.unInstaller = callBack;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DependencyInstaller onInitialize(CallBack initializeCallBack) {
|
||||
this.initializer = initializeCallBack;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngine;
|
||||
import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
|
||||
import org.hswebframework.ezorm.rdb.codec.ClobValueCodec;
|
||||
import org.hswebframework.ezorm.rdb.codec.CompositeValueCodec;
|
||||
import org.hswebframework.ezorm.rdb.codec.JsonValueCodec;
|
||||
import org.hswebframework.ezorm.rdb.mapping.SyncRepository;
|
||||
import org.hswebframework.ezorm.rdb.mapping.defaults.record.Record;
|
||||
import org.hswebframework.ezorm.rdb.operator.DatabaseOperator;
|
||||
import org.hswebframework.web.bean.FastBeanCopier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author zhouhao
|
||||
*/
|
||||
public class SystemInitialize {
|
||||
private final Logger logger = LoggerFactory.getLogger(SystemInitialize.class);
|
||||
|
||||
private final DatabaseOperator database;
|
||||
//将要安装的信息
|
||||
private final SystemVersion targetVersion;
|
||||
|
||||
//已安装的信息
|
||||
private SystemVersion installed;
|
||||
|
||||
private List<SimpleDependencyInstaller> readyToInstall = new ArrayList<>();
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private List<String> excludeTables;
|
||||
|
||||
private String installScriptPath = "classpath*:hsweb-starter.js";
|
||||
|
||||
private Map<String, Object> scriptContext = new HashMap<>();
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
private SyncRepository<Record, String> system;
|
||||
|
||||
public SystemInitialize(DatabaseOperator database, SystemVersion targetVersion) {
|
||||
this.database = database;
|
||||
this.targetVersion = targetVersion;
|
||||
}
|
||||
|
||||
|
||||
public void init() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
// if (!CollectionUtils.isEmpty(excludeTables)) {
|
||||
// this.database = new SkipCreateOrAlterRDBDatabase(database, excludeTables, sqlExecutor);
|
||||
// }
|
||||
|
||||
scriptContext.put("database", database);
|
||||
scriptContext.put("logger", logger);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
public void addScriptContext(String var, Object val) {
|
||||
scriptContext.put(var, val);
|
||||
}
|
||||
|
||||
protected void syncSystemVersion() {
|
||||
Map<String, Object> mapVersion = FastBeanCopier.copy(targetVersion, HashMap::new);
|
||||
|
||||
if (installed == null) {
|
||||
system.insert(Record.newRecord(mapVersion));
|
||||
} else {
|
||||
|
||||
//合并已安装的依赖
|
||||
//修复如果删掉了依赖,再重启会丢失依赖信息的问题
|
||||
for (Dependency dependency : installed.getDependencies()) {
|
||||
Dependency target = targetVersion.getDependency(dependency.getGroupId(), dependency.getArtifactId());
|
||||
if (target == null) {
|
||||
targetVersion.getDependencies().add(dependency);
|
||||
}
|
||||
}
|
||||
mapVersion = FastBeanCopier.copy(targetVersion, HashMap::new);
|
||||
system.createUpdate().set(Record.newRecord(mapVersion))
|
||||
.where(dsl -> dsl.is(targetVersion::getName))
|
||||
.execute();
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> getScriptContext() {
|
||||
return new HashMap<>(scriptContext);
|
||||
}
|
||||
|
||||
protected void doInstall() {
|
||||
List<SimpleDependencyInstaller> doInitializeDep = new ArrayList<>();
|
||||
List<Dependency> installedDependencies =
|
||||
readyToInstall.stream().map(installer -> {
|
||||
Dependency dependency = installer.getDependency();
|
||||
Dependency installed = getInstalledDependency(dependency.getGroupId(), dependency.getArtifactId());
|
||||
//安装依赖
|
||||
if (installed == null) {
|
||||
doInitializeDep.add(installer);
|
||||
installer.doInstall(getScriptContext());
|
||||
}
|
||||
//更新依赖
|
||||
if (installed == null || installed.compareTo(dependency) < 0) {
|
||||
installer.doUpgrade(getScriptContext(), installed);
|
||||
}
|
||||
return dependency;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
for (SimpleDependencyInstaller installer : doInitializeDep) {
|
||||
installer.doInitialize(getScriptContext());
|
||||
}
|
||||
targetVersion.setDependencies(installedDependencies);
|
||||
}
|
||||
|
||||
private Dependency getInstalledDependency(String groupId, String artifactId) {
|
||||
if (installed == null) {
|
||||
return null;
|
||||
}
|
||||
return installed.getDependency(groupId, artifactId);
|
||||
}
|
||||
|
||||
private SimpleDependencyInstaller getReadyToInstallDependency(String groupId, String artifactId) {
|
||||
if (readyToInstall == null) {
|
||||
return null;
|
||||
}
|
||||
return readyToInstall.stream()
|
||||
.filter(installer -> installer.getDependency().isSameDependency(groupId, artifactId))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private void initReadyToInstallDependencies() {
|
||||
DynamicScriptEngine engine = DynamicScriptEngineFactory.getEngine("js");
|
||||
try {
|
||||
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(installScriptPath);
|
||||
List<SimpleDependencyInstaller> installers = new ArrayList<>();
|
||||
for (Resource resource : resources) {
|
||||
String script = StreamUtils.copyToString(resource.getInputStream(), Charset.forName("utf-8"));
|
||||
SimpleDependencyInstaller installer = new SimpleDependencyInstaller();
|
||||
engine.compile("__tmp", script);
|
||||
Map<String, Object> context = getScriptContext();
|
||||
context.put("dependency", installer);
|
||||
engine.execute("__tmp", context).getIfSuccess();
|
||||
installers.add(installer);
|
||||
}
|
||||
readyToInstall = installers;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
engine.remove("__tmp");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void initInstallInfo() {
|
||||
boolean tableInstall = database.getMetadata().getTable("s_system").isPresent();
|
||||
database.ddl().createOrAlter("s_system")
|
||||
.addColumn().name("name").varchar(128).comment("系统名称").commit()
|
||||
.addColumn().name("major_version").alias("majorVersion").integer().comment("主版本号").commit()
|
||||
.addColumn().name("minor_version").alias("minorVersion").integer().comment("次版本号").commit()
|
||||
.addColumn().name("revision_version").alias("revisionVersion").integer().comment("修订版").commit()
|
||||
.addColumn().name("comment").varchar(2000).comment("系统说明").commit()
|
||||
.addColumn().name("website").varchar(2000).comment("系统网址").commit()
|
||||
.addColumn().name("framework_version").notNull().alias("frameworkVersion").clob()
|
||||
.custom(column ->
|
||||
column.setValueCodec(new CompositeValueCodec()
|
||||
.addEncoder(JsonValueCodec.of(SystemVersion.FrameworkVersion.class))
|
||||
.addDecoder(ClobValueCodec.INSTANCE)
|
||||
.addDecoder(JsonValueCodec.of(SystemVersion.FrameworkVersion.class)))).notNull().comment("框架版本").commit()
|
||||
.addColumn().name("dependencies").notNull().alias("dependencies").clob()
|
||||
.custom(column -> column.setValueCodec(new CompositeValueCodec()
|
||||
.addEncoder(JsonValueCodec.ofCollection(List.class, Dependency.class))
|
||||
.addDecoder(ClobValueCodec.INSTANCE)
|
||||
.addDecoder(JsonValueCodec.ofCollection(List.class, Dependency.class)))).notNull().comment("依赖详情").commit()
|
||||
.comment("系统信息")
|
||||
.allowAlter(false)
|
||||
.commit()
|
||||
.sync();
|
||||
system = database.dml().createRepository("s_system");
|
||||
|
||||
if (!tableInstall) {
|
||||
installed = null;
|
||||
return;
|
||||
}
|
||||
|
||||
installed = system.createQuery()
|
||||
.where(dsl -> dsl.is("name", targetVersion.getName()))
|
||||
.paging(0, 1)
|
||||
.fetchOne()
|
||||
.map(r -> FastBeanCopier.copy(r, SystemVersion::new))
|
||||
.orElse(null)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
public void install() throws Exception {
|
||||
init();
|
||||
initInstallInfo();
|
||||
initReadyToInstallDependencies();
|
||||
doInstall();
|
||||
syncSystemVersion();
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright 2019 http://www.hswebframework.org
|
||||
* *
|
||||
* * 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 org.hswebframework.web.starter.initialize;
|
||||
|
||||
import org.hswebframework.utils.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SystemVersion extends Version {
|
||||
|
||||
public SystemVersion() {
|
||||
}
|
||||
|
||||
public SystemVersion(String version) {
|
||||
this.setVersion(version);
|
||||
}
|
||||
|
||||
private FrameworkVersion frameworkVersion = new FrameworkVersion();
|
||||
|
||||
private List<Dependency> dependencies = new ArrayList<>();
|
||||
|
||||
public FrameworkVersion getFrameworkVersion() {
|
||||
return frameworkVersion;
|
||||
}
|
||||
|
||||
public void setFrameworkVersion(FrameworkVersion frameworkVersion) {
|
||||
this.frameworkVersion = frameworkVersion;
|
||||
}
|
||||
|
||||
public List<Dependency> getDependencies() {
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
public void setDependencies(List<Dependency> dependencies) {
|
||||
this.dependencies = dependencies;
|
||||
initDepCache();
|
||||
}
|
||||
|
||||
private Map<String, Dependency> depCache;
|
||||
|
||||
protected String getDepKey(String groupId, String artifactId) {
|
||||
return StringUtils.concat(groupId, "/", artifactId);
|
||||
}
|
||||
|
||||
protected void initDepCache() {
|
||||
depCache = new HashMap<>();
|
||||
dependencies.forEach(dependency -> depCache.put(getDepKey(dependency.groupId, dependency.artifactId), dependency));
|
||||
}
|
||||
|
||||
public Dependency getDependency(String groupId, String artifactId) {
|
||||
if (depCache == null) {
|
||||
initDepCache();
|
||||
}
|
||||
return depCache.get(getDepKey(groupId, artifactId));
|
||||
}
|
||||
|
||||
public static class FrameworkVersion extends Version {
|
||||
public FrameworkVersion() {
|
||||
setName("hsweb framework");
|
||||
setComment("企业后台管理系统基础框架");
|
||||
setWebsite("http://www.hsweb.io");
|
||||
setComment("");
|
||||
setVersion(4, 0, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface Property {
|
||||
/**
|
||||
* @see SystemVersion#name
|
||||
*/
|
||||
String name = "name";
|
||||
/**
|
||||
* @see SystemVersion#comment
|
||||
*/
|
||||
String comment = "comment";
|
||||
/**
|
||||
* @see SystemVersion#website
|
||||
*/
|
||||
String website = "website";
|
||||
/**
|
||||
* @see SystemVersion#majorVersion
|
||||
*/
|
||||
String majorVersion = "majorVersion";
|
||||
/**
|
||||
* @see SystemVersion#minorVersion
|
||||
*/
|
||||
String minorVersion = "minorVersion";
|
||||
/**
|
||||
* @see SystemVersion#revisionVersion
|
||||
*/
|
||||
String revisionVersion = "revisionVersion";
|
||||
/**
|
||||
* @see SystemVersion#snapshot
|
||||
*/
|
||||
String snapshot = "snapshot";
|
||||
|
||||
/**
|
||||
* @see SystemVersion#frameworkVersion
|
||||
*/
|
||||
String frameworkVersion = "frameworkVersion";
|
||||
|
||||
/**
|
||||
* @see SystemVersion#dependencies
|
||||
*/
|
||||
String dependencies = "dependencies";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
package org.hswebframework.web.starter.initialize;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.hswebframework.utils.ListUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Slf4j
|
||||
public class Version implements Comparable<Version>, Serializable {
|
||||
protected String name;
|
||||
protected String comment;
|
||||
protected String website;
|
||||
protected int majorVersion = 1;
|
||||
protected int minorVersion = 0;
|
||||
protected int revisionVersion = 0;
|
||||
protected boolean snapshot = false;
|
||||
|
||||
public void setVersion(int major, int minor, int revision, boolean snapshot) {
|
||||
this.majorVersion = major;
|
||||
this.minorVersion = minor;
|
||||
this.revisionVersion = revision;
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
if (null == version) {
|
||||
return;
|
||||
}
|
||||
version = version.toLowerCase();
|
||||
|
||||
boolean snapshot = version.toLowerCase().contains("snapshot");
|
||||
|
||||
String[] ver = version.split("[-]")[0].split("[.]");
|
||||
Integer[] numberVer = ListUtils.stringArr2intArr(ver);
|
||||
if (numberVer.length == 0) {
|
||||
numberVer = new Integer[]{1, 0, 0};
|
||||
log.warn("解析版本号失败:{},将使用默认版本号:1.0.0,请检查hsweb-starter.js配置内容!", version);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numberVer.length; i++) {
|
||||
if (numberVer[i] == null) {
|
||||
numberVer[i] = 0;
|
||||
}
|
||||
}
|
||||
setVersion(numberVer[0],
|
||||
numberVer.length <= 1 ? 0 : numberVer[1],
|
||||
numberVer.length <= 2 ? 0 : numberVer[2],
|
||||
snapshot);
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
|
||||
public void setComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
public String getWebsite() {
|
||||
if (website == null) {
|
||||
website = "";
|
||||
}
|
||||
return website;
|
||||
}
|
||||
|
||||
public void setWebsite(String website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
public int getMajorVersion() {
|
||||
return majorVersion;
|
||||
}
|
||||
|
||||
public void setMajorVersion(int majorVersion) {
|
||||
this.majorVersion = majorVersion;
|
||||
}
|
||||
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
public void setMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
}
|
||||
|
||||
public int getRevisionVersion() {
|
||||
return revisionVersion;
|
||||
}
|
||||
|
||||
public void setRevisionVersion(int revisionVersion) {
|
||||
this.revisionVersion = revisionVersion;
|
||||
}
|
||||
|
||||
public boolean isSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public void setSnapshot(boolean snapshot) {
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Version o) {
|
||||
if (null == o) {
|
||||
return -1;
|
||||
}
|
||||
if (o.getMajorVersion() > this.getMajorVersion()) {
|
||||
return -1;
|
||||
}
|
||||
if (o.getMajorVersion() == this.getMajorVersion()) {
|
||||
if (o.getMinorVersion() > this.getMinorVersion()) {
|
||||
return -1;
|
||||
}
|
||||
if (o.getMinorVersion() == this.getMinorVersion()) {
|
||||
return Integer.compare(this.getRevisionVersion(), o.getRevisionVersion());
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public String versionToString() {
|
||||
return String.valueOf(majorVersion) + "." +
|
||||
minorVersion + "." +
|
||||
revisionVersion + (snapshot ? "-SNAPSHOT" : "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + " version " +
|
||||
majorVersion + "." +
|
||||
minorVersion + "." +
|
||||
revisionVersion + (snapshot ? "-SNAPSHOT" : "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import org.springframework.http.converter.json.MappingJackson2HttpMessageConvert
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
public class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
org.hswebframework.web.starter.jackson.CustomCodecsAutoConfiguration
|
||||
org.hswebframework.web.starter.HswebAutoConfiguration
|
||||
org.hswebframework.web.starter.CorsAutoConfiguration
|
||||
org.hswebframework.web.starter.i18n.I18nConfiguration
|
||||
@@ -20,8 +20,7 @@ public class SystemInitializeTest {
|
||||
|
||||
@Test
|
||||
public void test(){
|
||||
Assert.assertTrue(databaseOperator.getMetadata().getTable("s_user").isPresent());
|
||||
|
||||
assertNotNull(databaseOperator);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,8 +14,8 @@ import org.hswebframework.web.validator.CreateGroup;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.sql.JDBCType;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -14,8 +14,8 @@ import javax.persistence.Column;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.sql.JDBCType;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.hswebframework.web.validator.CreateGroup;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
||||
@@ -16,7 +16,7 @@ import org.springframework.util.StringUtils;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import java.sql.JDBCType;
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -16,8 +16,8 @@ import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import java.sql.JDBCType;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user