├── opensabre-starter-config ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── opensabre-config.yml │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── config │ │ └── OpensabreConfig.java └── pom.xml ├── opensabre-starter-boot ├── src │ ├── main │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ ├── spring.factories │ │ │ │ ├── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ ├── spring-configuration-metadata.json │ │ │ │ └── additional-spring-configuration-metadata.json │ │ │ ├── bootstrap.yaml │ │ │ ├── banner.txt │ │ │ ├── opensabre-boot.properties │ │ │ └── logback-spring.xml │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── opensabre │ │ │ └── boot │ │ │ ├── annotations │ │ │ ├── OperationType.java │ │ │ ├── EnabledAudit.java │ │ │ ├── Audit.java │ │ │ └── Desensitization.java │ │ │ ├── event │ │ │ ├── AuditEvent.java │ │ │ ├── MappingRegisteredEvent.java │ │ │ ├── DefaultAuditEventHandler.java │ │ │ ├── OpensabreSensitiveDesensitizerProcessor.java │ │ │ └── OpensabreStartedEventHandler.java │ │ │ ├── sensitive │ │ │ ├── log │ │ │ │ ├── strategy │ │ │ │ │ ├── DesensitizerStrategy.java │ │ │ │ │ └── DefaultDesensitizerStrategy.java │ │ │ │ ├── desensitizer │ │ │ │ │ ├── NameLogBackDesensitizer.java │ │ │ │ │ ├── PasswordLogBackDesensitizer.java │ │ │ │ │ ├── LogBackDesensitizer.java │ │ │ │ │ ├── AbstractLogBackDesensitizer.java │ │ │ │ │ ├── PrefixLogBackDesensitizer.java │ │ │ │ │ └── RegxLogBackDesensitizer.java │ │ │ │ └── LogBackCoreConverter.java │ │ │ ├── rest │ │ │ │ ├── strategy │ │ │ │ │ ├── SensitiveStrategy.java │ │ │ │ │ ├── CustomSensitiveStrategy.java │ │ │ │ │ └── DefaultSensitiveStrategy.java │ │ │ │ └── DesensitizationSerialize.java │ │ │ └── rule │ │ │ │ ├── SensitiveRule.java │ │ │ │ ├── CustomSensitiveRule.java │ │ │ │ └── DefaultSensitiveRule.java │ │ │ ├── config │ │ │ ├── OpensabreWebConfiguration.java │ │ │ ├── OpensabreServiceConfig.java │ │ │ ├── OpensabreBootConfig.java │ │ │ ├── YamlPropertyLoaderFactory.java │ │ │ ├── OpensabreSensitiveConfig.java │ │ │ ├── OpensabreSwaggerConfig.java │ │ │ └── OpensabreEnvConfig.java │ │ │ ├── entity │ │ │ ├── RestMappingInfo.java │ │ │ ├── SwaggerInfo.java │ │ │ └── AuditInfo.java │ │ │ ├── metadata │ │ │ ├── OpensabreVersion.java │ │ │ └── OpensabreCloud.java │ │ │ └── rest │ │ │ └── MappingInfoHandler.java │ └── test │ │ ├── java │ │ └── io │ │ │ └── github │ │ │ └── opensabre │ │ │ └── boot │ │ │ ├── crypt │ │ │ └── JasyptTest.java │ │ │ └── sensitive │ │ │ └── log │ │ │ └── desensitizer │ │ │ ├── NameLogBackDesensitizerTest.java │ │ │ ├── PasswordLogBackDesensitizerTest.java │ │ │ └── RegxLogBackDesensitizerTest.java │ │ └── resources │ │ └── application.yml └── pom.xml ├── opensabre-starter-cache ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── opensabre-cache.properties │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── cache │ │ └── redis │ │ ├── DefaultCacheArea.java │ │ └── JetCacheConfig.java └── pom.xml ├── opensabre-starter-eda ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── opensabre-eda.yml │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── eda │ │ └── config │ │ └── OpensabreEdaConfig.java └── pom.xml ├── opensabre-starter-persistence ├── src │ ├── main │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ ├── spring │ │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ ├── spring-configuration-metadata.json │ │ │ │ └── additional-spring-configuration-metadata.json │ │ │ └── opensabre-persistence.properties │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── opensabre │ │ │ └── persistence │ │ │ ├── exception │ │ │ └── PersistenceExceptionHandlerAdvice.java │ │ │ ├── entity │ │ │ ├── param │ │ │ │ └── BaseParam.java │ │ │ ├── form │ │ │ │ └── BaseQueryForm.java │ │ │ └── po │ │ │ │ └── BasePo.java │ │ │ ├── handler │ │ │ └── PoMetaObjectHandler.java │ │ │ └── config │ │ │ └── MybatisConfig.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── persistence │ │ ├── entity │ │ ├── vo │ │ │ └── UserVo.java │ │ ├── param │ │ │ └── UserParam.java │ │ ├── po │ │ │ └── User.java │ │ ├── form │ │ │ ├── UserForm.java │ │ │ └── UserQueryForm.java │ │ └── BaseEntityTest.java │ │ └── exception │ │ └── PersistenceExceptionHandlerAdviceTest.java └── pom.xml ├── .gitignore ├── README.md ├── opensabre-web ├── src │ ├── main │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── opensabre │ │ │ └── common │ │ │ ├── core │ │ │ ├── exception │ │ │ │ ├── ServiceException.java │ │ │ │ ├── ErrorType.java │ │ │ │ ├── BaseException.java │ │ │ │ └── SystemErrorType.java │ │ │ ├── util │ │ │ │ └── UserContextHolder.java │ │ │ └── entity │ │ │ │ └── vo │ │ │ │ └── Result.java │ │ │ └── web │ │ │ ├── entity │ │ │ ├── convert │ │ │ │ ├── EntityConverter.java │ │ │ │ └── EntityModelConverter.java │ │ │ ├── vo │ │ │ │ └── BaseVo.java │ │ │ └── form │ │ │ │ └── BaseForm.java │ │ │ ├── validator │ │ │ ├── Mobile.java │ │ │ ├── MobileValidator.java │ │ │ ├── EnumStringValidator.java │ │ │ └── EnumString.java │ │ │ ├── interceptor │ │ │ └── UserInterceptor.java │ │ │ ├── rest │ │ │ └── RestResponseBodyAdvice.java │ │ │ └── exception │ │ │ └── DefaultGlobalExceptionHandlerAdvice.java │ └── test │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── common │ │ ├── web │ │ ├── interceptor │ │ │ └── UserInterceptorTest.java │ │ └── exception │ │ │ └── DefaultGlobalExceptionHandlerAdviceTest.java │ │ └── core │ │ ├── exception │ │ └── BaseExceptionTest.java │ │ ├── util │ │ └── UserContextHolderTest.java │ │ └── entity │ │ └── vo │ │ └── ResultTest.java ├── readme.md └── pom.xml ├── opensabre-starter-rpc ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── opensabre-rpc.yml │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── rpc │ │ ├── sentinel │ │ ├── config │ │ │ └── OpensabreSentinelConfig.java │ │ └── exception │ │ │ └── SentinelExceptionHandlerAdvice.java │ │ ├── loadbalance │ │ ├── balance │ │ │ └── Balance.java │ │ ├── router │ │ │ └── Router.java │ │ └── OpensabreLoadBalancer.java │ │ └── openfeign │ │ ├── config │ │ ├── OpensabreFeignConfig.java │ │ ├── OpensabreLoadBalancerConfig.java │ │ └── OpensabreLoadBalancerBean.java │ │ └── interceptor │ │ └── FeignHeaderInterceptor.java └── pom.xml ├── opensabre-starter-register ├── src │ └── main │ │ ├── resources │ │ ├── opensabre-register.yml │ │ └── META-INF │ │ │ └── spring │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── register │ │ └── config │ │ ├── OpensabreDiscoveryConfiguration.java │ │ ├── OpensabreNacosDiscoveryConfiguration.java │ │ ├── Metadata.java │ │ └── OpensabreNacosProjectConfiguration.java └── pom.xml ├── opensabre-test ├── src │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── opensabre │ │ │ └── common │ │ │ └── test │ │ │ ├── PrivateObject.java │ │ │ └── PrivateHelperTest.java │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── common │ │ └── test │ │ └── PrivateHelper.java └── pom.xml ├── opensabre-starter-webmvc ├── readme.md ├── src │ ├── test │ │ └── java │ │ │ └── io │ │ │ └── github │ │ │ └── opensabre │ │ │ └── webmvc │ │ │ └── interceptor │ │ │ └── UserInterceptorTest.java │ └── main │ │ └── java │ │ └── io │ │ └── github │ │ └── opensabre │ │ └── webmvc │ │ └── exception │ │ └── DefaultWebMvcExceptionHandlerAdvice.java └── pom.xml ├── .github └── workflows │ ├── maven.yml │ └── release.yml └── CLAUDE.md /opensabre-starter-config/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.config.OpensabreConfig -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.env.EnvironmentPostProcessor=io.github.opensabre.boot.config.OpensabreEnvConfig -------------------------------------------------------------------------------- /opensabre-starter-cache/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.cache.redis.JetCacheConfig -------------------------------------------------------------------------------- /opensabre-starter-eda/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.eda.config.OpensabreEdaConfig 2 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.persistence.config.MybatisConfig 2 | -------------------------------------------------------------------------------- /opensabre-starter-config/src/main/resources/opensabre-config.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | nacos: 4 | config: 5 | server-addr: ${REGISTER_HOST:localhost}:${REGISTER_PORT:8848} 6 | file-extension: yml -------------------------------------------------------------------------------- /opensabre-starter-eda/src/main/resources/opensabre-eda.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | rabbitmq: 3 | host: ${RABBIT_MQ_HOST:localhost} 4 | port: ${RABBIT_MQ_PORT:5672} 5 | username: ${RABBIT_MQ_USERNAME:guest} 6 | password: ${RABBIT_MQ_PASSWORD:guest} -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/annotations/OperationType.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.annotations; 2 | 3 | public enum OperationType { 4 | CREATE, UPDATE, DELETE, QUERY, LOGIN, LOGOUT, SCAN, EXPORT, IMPORT, DOWNLOAD, UPLOAD 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data 2 | logs 3 | target/ 4 | pom-xml-flattened 5 | .vscode/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .DS_Store 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 💪Opensabre是基于SpringCloud2023的微服务开发平台,整合了Spring Security、Springcloud Alibaba等组件。 2 | 3 | 包含了基础的RBAC权限管理、授权认证、网关管理、服务治理、审计日志等系统管理基础应用。 4 | 5 | 定义了相关开发规范、风格并落地在服务框架层,开箱即用,支持Docker、Kubenetes的部署。 6 | 7 | 让项目开发人员快速进入业务开发,而不需过多时间花费在基础架构搭建和编码风格规范上。 8 | 9 | 目标是建立一套金融级、高安全性的微服务解决方案。 10 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/core/exception/ServiceException.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.exception; 2 | 3 | /** 4 | * Created by zhoutaoo on 2018/6/2. 5 | */ 6 | public class ServiceException extends BaseException { 7 | //TODO 对业务异常的返回码进行校验,规范到一定范围内 8 | } 9 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.rpc.openfeign.config.OpensabreFeignConfig 2 | io.github.opensabre.rpc.openfeign.config.OpensabreLoadBalancerConfig 3 | io.github.opensabre.rpc.sentinel.config.OpensabreSentinelConfig -------------------------------------------------------------------------------- /opensabre-starter-register/src/main/resources/opensabre-register.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | nacos: 4 | discovery: 5 | enabled: true 6 | server-addr: ${REGISTER_HOST:localhost}:${REGISTER_PORT:8848} 7 | cluster-name: CLUSTER-${CLOUD_AZ:LOCAL} # 取环境变量AZ做为部分集群名:CLUSTER-SH,无环境变量默认为CLUSTER-LOCAL代表本地开发实例 -------------------------------------------------------------------------------- /opensabre-starter-register/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.register.config.OpensabreDiscoveryConfiguration 2 | io.github.opensabre.register.config.OpensabreNacosDiscoveryConfiguration 3 | io.github.opensabre.register.config.OpensabreNacosProjectConfiguration 4 | -------------------------------------------------------------------------------- /opensabre-starter-cache/src/main/java/io/github/opensabre/cache/redis/DefaultCacheArea.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.cache.redis; 2 | 3 | /** 4 | * 默认缓存区域 5 | * longTime 长时间缓存 6 | * shortTime 短时缓存 7 | */ 8 | public interface DefaultCacheArea { 9 | String LONG_TIME_AREA = "longTime"; 10 | String SHORT_TIME_AREA = "shortTime"; 11 | } 12 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/entity/vo/UserVo.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.vo; 2 | 3 | import io.github.opensabre.common.web.entity.vo.BaseVo; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class UserVo extends BaseVo { 8 | private String name; 9 | private String sex; 10 | } 11 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/event/AuditEvent.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | /** 6 | * 审计事件 7 | */ 8 | public class AuditEvent extends ApplicationEvent { 9 | public AuditEvent(Object source) { 10 | super(source); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/bootstrap.yaml: -------------------------------------------------------------------------------- 1 | #==============日志===============# 2 | logging: 3 | level: 4 | io.github.opensabre: info 5 | logback: 6 | rollingpolicy: 7 | max-file-size: 1GB 8 | file: 9 | path: logs 10 | pattern: 11 | level: '%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-},%X{parentSpanId:-}]' 12 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | io.github.opensabre.boot.config.OpensabreSwaggerConfig 2 | io.github.opensabre.boot.config.OpensabreBootConfig 3 | io.github.opensabre.boot.config.OpensabreWebConfiguration 4 | io.github.opensabre.boot.config.OpensabreServiceConfig 5 | io.github.opensabre.boot.config.OpensabreSensitiveConfig 6 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/entity/param/UserParam.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.param; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | import lombok.ToString; 6 | 7 | @Data 8 | @ToString 9 | @NoArgsConstructor 10 | public class UserParam extends BaseParam { 11 | private String name; 12 | } 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/strategy/DesensitizerStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.strategy; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | 5 | public interface DesensitizerStrategy { 6 | /** 7 | * @param event 日志事件 8 | * @return 脱敏后的日志字串 9 | */ 10 | String desensitizing(ILoggingEvent event); 11 | } 12 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/event/MappingRegisteredEvent.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | /** 6 | * 自定义Spring事件,用于Rest信息以事件方式发送出去 7 | */ 8 | public class MappingRegisteredEvent extends ApplicationEvent { 9 | public MappingRegisteredEvent(Object source) { 10 | super(source); 11 | } 12 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/OpensabreWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | import org.springframework.boot.autoconfigure.AutoConfiguration; 4 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 5 | 6 | /** 7 | * Web配置 8 | */ 9 | @AutoConfiguration 10 | public class OpensabreWebConfiguration implements WebMvcConfigurer { 11 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/core/exception/ErrorType.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.exception; 2 | 3 | /** 4 | * 错误类型接口 5 | */ 6 | public interface ErrorType { 7 | /** 8 | * 返回code 9 | * 10 | * @return 错误code 11 | */ 12 | String getCode(); 13 | 14 | /** 15 | * 返回mesg 16 | * 17 | * @return 错误信息 18 | */ 19 | String getMesg(); 20 | } 21 | -------------------------------------------------------------------------------- /opensabre-web/readme.md: -------------------------------------------------------------------------------- 1 | WEB公共包 2 | ---------- 3 | 4 | ## 简介 5 | 6 | 主要封装一些WEB开发用到的通用公共类、工具类,如公共web拦截器、web统一异常定义等。 7 | 8 | ## 使用 9 | 10 | 进入应用目录 11 | 12 | 安装命令:`mvn install` 13 | 14 | ## 使用指南 15 | 16 | ### 应用引入 17 | 18 | 需要将编译生成的jar包安装到本地maven类进入引用使用。 19 | 20 | pom.xml 21 | 22 | ``` 23 | 24 | io.github.opensabre 25 | common-web 26 | 0.0.1 27 | 28 | ``` -------------------------------------------------------------------------------- /opensabre-test/src/test/java/io/github/opensabre/common/test/PrivateObject.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.test; 2 | 3 | public class PrivateObject { 4 | private String code; 5 | 6 | public String getCode() { 7 | return code; 8 | } 9 | 10 | private String changeCode(String code) { 11 | this.code = code; 12 | return this.code; 13 | } 14 | 15 | private void changeCode() { 16 | this.code = "aaaaa"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/sentinel/config/OpensabreSentinelConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.sentinel.config; 2 | 3 | import io.github.opensabre.rpc.sentinel.exception.SentinelExceptionHandlerAdvice; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.context.annotation.Import; 6 | 7 | @AutoConfiguration 8 | @Import({SentinelExceptionHandlerAdvice.class}) 9 | public class OpensabreSentinelConfig { 10 | } -------------------------------------------------------------------------------- /opensabre-starter-webmvc/readme.md: -------------------------------------------------------------------------------- 1 | WEBMVC公共包 2 | ---------- 3 | 4 | ## 简介 5 | 6 | 主要封装spring-webmvc WEB开发用到的通用公共类、工具类,如公共web拦截器、web统一异常定义等。 7 | 8 | ## 使用 9 | 10 | 进入应用目录 11 | 12 | 安装命令:`mvn install` 13 | 14 | ## 使用指南 15 | 16 | ### 应用引入 17 | 18 | 需要将编译生成的jar包安装到本地maven类进入引用使用。 19 | 20 | pom.xml 21 | 22 | ``` 23 | 24 | io.github.opensabre 25 | opensabre-starter-webmvc 26 | 0.1.1 27 | 28 | ``` -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/loadbalance/balance/Balance.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.loadbalance.balance; 2 | 3 | import org.springframework.cloud.client.ServiceInstance; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 负载均衡分发器 9 | * 主要为工作分发器,从一众节点中选择一个 10 | */ 11 | public interface Balance { 12 | /** 13 | * @param instances 所有可用实例 14 | * @return ServiceInstance 15 | */ 16 | ServiceInstance choose(List instances); 17 | } 18 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/loadbalance/router/Router.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.loadbalance.router; 2 | 3 | import org.springframework.cloud.client.ServiceInstance; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 路由算法接口 9 | * 主要用于筛选实例,从一众节点中筛选出符合要求的节点 10 | */ 11 | public interface Router { 12 | /** 13 | * @param instances 所有可用实例 14 | * @return ServiceInstance列表 15 | */ 16 | List routing(List instances); 17 | } 18 | -------------------------------------------------------------------------------- /opensabre-starter-config/src/main/java/io/github/opensabre/config/OpensabreConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.config; 2 | 3 | import io.github.opensabre.boot.config.YamlPropertyLoaderFactory; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.context.annotation.PropertySource; 6 | 7 | @AutoConfiguration 8 | @PropertySource(value = {"classpath:opensabre-config.yml"}, encoding = "UTF8", factory = YamlPropertyLoaderFactory.class) 9 | public class OpensabreConfig { 10 | } 11 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rest/strategy/SensitiveStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rest.strategy; 2 | 3 | import io.github.opensabre.boot.sensitive.rule.SensitiveRule; 4 | 5 | /** 6 | * 脱敏策略接口 7 | */ 8 | public interface SensitiveStrategy { 9 | /** 10 | * 脱敏处理 11 | * 12 | * @param type 类型 13 | * @param originStr 原始字符串 14 | * @return 脱敏后的字符 15 | */ 16 | String desensitizing(SensitiveRule type, String originStr); 17 | } 18 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/entity/convert/EntityConverter.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.entity.convert; 2 | 3 | /** 4 | * 对象转换 5 | * Form -》 Po 6 | * QueryForm -》 Param 7 | */ 8 | public interface EntityConverter { 9 | /** 10 | * 对象转换器接口 11 | * 12 | * @param source 源对象 13 | * @param entityClass 目标对象类 14 | * @param 源对象类型 15 | * @param

目标对象类型 16 | * @return P对象 17 | */ 18 | P convert(F source, Class

entityClass); 19 | } 20 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/entity/po/User.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.po; 2 | 3 | import io.github.opensabre.persistence.entity.vo.UserVo; 4 | import lombok.*; 5 | 6 | import java.util.Date; 7 | 8 | @Data 9 | @NoArgsConstructor 10 | @RequiredArgsConstructor 11 | @EqualsAndHashCode(callSuper = true) 12 | public class User extends BasePo { 13 | @NonNull 14 | private String name; 15 | private Integer age; 16 | private String sex; 17 | private Date birthday; 18 | } -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/openfeign/config/OpensabreFeignConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.openfeign.config; 2 | 3 | import io.github.opensabre.boot.config.YamlPropertyLoaderFactory; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.context.annotation.PropertySource; 6 | 7 | @AutoConfiguration 8 | @PropertySource(value = {"classpath:opensabre-rpc.yml"}, encoding = "UTF8", factory = YamlPropertyLoaderFactory.class) 9 | public class OpensabreFeignConfig { 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: Java CI 2 | # 触发脚本的事件 这里为push代码之后触发 3 | on: [push] 4 | # 定义一个发行任务 5 | jobs: 6 | build: 7 | # 任务运行的环境 8 | runs-on: ubuntu-latest 9 | # 任务的步骤 10 | steps: 11 | # 1. 声明 checkout 仓库代码到工作区 12 | - uses: actions/checkout@v2 13 | # 2. 安装Java 环境 这里会用到的参数就是 Git Action secrets中配置的, 14 | - name: Set up JDK 17 15 | uses: actions/setup-java@v1 16 | with: 17 | java-version: 17 18 | # 3.编译打包 19 | - name: Build with Maven 20 | run: mvn -B package javadoc:javadoc --file pom.xml -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/desensitizer/NameLogBackDesensitizer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule; 4 | 5 | /** 6 | * 姓名脱敏器 7 | * 日志中形如 姓名:张三 / name=李四 等形如此类的中文姓名敏感数据进行屏蔽 8 | * 9 | * @author zhoutaoo 10 | */ 11 | public class NameLogBackDesensitizer extends PrefixLogBackDesensitizer { 12 | 13 | public NameLogBackDesensitizer() { 14 | super(DefaultSensitiveRule.NAME, 3); 15 | 16 | } 17 | } -------------------------------------------------------------------------------- /opensabre-starter-cache/src/main/java/io/github/opensabre/cache/redis/JetCacheConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.cache.redis; 2 | 3 | import com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.context.annotation.PropertySource; 6 | 7 | /** 8 | * 打开Redis缓存配置类 9 | */ 10 | @AutoConfiguration(before = JetCacheAutoConfiguration.class) 11 | @PropertySource(value = "classpath:opensabre-cache.properties", encoding = "UTF8") 12 | public class JetCacheConfig { 13 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/entity/RestMappingInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.entity; 2 | 3 | import lombok.Data; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | /** 8 | * Rest注册信息 9 | */ 10 | @Data 11 | @RequiredArgsConstructor 12 | public class RestMappingInfo { 13 | /** 14 | * Rest 的path url,如:/user/{name} 15 | */ 16 | @NonNull 17 | private String url; 18 | /** 19 | * Rest 的方法,如:GET/POST .. 20 | */ 21 | @NonNull 22 | private String method; 23 | } 24 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/event/DefaultAuditEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.event; 2 | 3 | import lombok.NonNull; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.context.ApplicationListener; 6 | 7 | /** 8 | * 默认 AuditEvent Listener 9 | */ 10 | @Slf4j 11 | public class DefaultAuditEventHandler implements ApplicationListener { 12 | 13 | @Override 14 | public void onApplicationEvent(@NonNull AuditEvent event) { 15 | log.info("AuditEvent received: {}", event); 16 | } 17 | } -------------------------------------------------------------------------------- /opensabre-starter-register/src/main/java/io/github/opensabre/register/config/OpensabreDiscoveryConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.register.config; 2 | 3 | import io.github.opensabre.boot.config.YamlPropertyLoaderFactory; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.context.annotation.PropertySource; 6 | 7 | @AutoConfiguration 8 | @PropertySource(value = {"classpath:opensabre-register.yml"}, encoding = "UTF8", factory = YamlPropertyLoaderFactory.class) 9 | public class OpensabreDiscoveryConfiguration { 10 | } 11 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/entity/vo/BaseVo.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.entity.vo; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | 9 | /** 10 | * VO对象的基类 11 | */ 12 | @Data 13 | @NoArgsConstructor 14 | public class BaseVo implements Serializable { 15 | /** 16 | * VO对象唯一id 17 | */ 18 | @Schema(title = "对象唯一id", description = "对象唯一id", requiredMode = Schema.RequiredMode.REQUIRED) 19 | private String id; 20 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/annotations/EnabledAudit.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.annotations; 2 | 3 | import io.github.opensabre.boot.aspect.AuditAspect; 4 | import io.github.opensabre.boot.event.DefaultAuditEventHandler; 5 | import org.springframework.context.annotation.Import; 6 | 7 | import java.lang.annotation.*; 8 | 9 | /** 10 | * 审计日志注解 11 | */ 12 | @Target(ElementType.TYPE) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | @Import({AuditAspect.class, DefaultAuditEventHandler.class}) 16 | public @interface EnabledAudit { 17 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/entity/SwaggerInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.entity; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | @Data 7 | @ConfigurationProperties(prefix = "opensabre.rest.swagger") 8 | public class SwaggerInfo { 9 | private String version; 10 | private String title; 11 | private String description; 12 | private String licenseUrl; 13 | private String licenseName; 14 | private String wikiUrl; 15 | private String wikiDocumentation; 16 | } 17 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/OpensabreServiceConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | 4 | import io.github.opensabre.boot.event.OpensabreStartedEventHandler; 5 | import io.github.opensabre.boot.rest.MappingInfoHandler; 6 | import org.springframework.boot.autoconfigure.AutoConfiguration; 7 | import org.springframework.context.annotation.Import; 8 | 9 | /** 10 | * Opensabre Rest信息事件通知配置类 11 | */ 12 | @AutoConfiguration 13 | @Import({OpensabreStartedEventHandler.class, MappingInfoHandler.class}) 14 | public class OpensabreServiceConfig { 15 | } 16 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/entity/form/UserForm.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.form; 2 | 3 | import io.github.opensabre.common.web.entity.form.BaseForm; 4 | import io.github.opensabre.persistence.entity.po.User; 5 | import lombok.*; 6 | 7 | import java.util.Date; 8 | 9 | @Data 10 | @NoArgsConstructor 11 | @RequiredArgsConstructor 12 | @EqualsAndHashCode(callSuper = true) 13 | public class UserForm extends BaseForm { 14 | @NonNull 15 | private String name; 16 | private Integer age; 17 | private String sex; 18 | private Date birthday; 19 | } 20 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/desensitizer/PasswordLogBackDesensitizer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule; 4 | 5 | /** 6 | * 密码的脱敏器 7 | * 在动态脱敏器没有初使化前,默认使用该脱敏器。 8 | * 主要对于系统启动时可能存在的敏感信息,如密码/凭证/key等。 9 | * 如 passwd:123456 / key=123456 等形如此类的敏感数据进行屏蔽 10 | * 11 | * @author zhoutaoo 12 | */ 13 | public class PasswordLogBackDesensitizer extends PrefixLogBackDesensitizer { 14 | 15 | public PasswordLogBackDesensitizer() { 16 | super(DefaultSensitiveRule.PASSWORD, 3); 17 | } 18 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/entity/form/UserQueryForm.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.form; 2 | 3 | import io.github.opensabre.persistence.entity.param.UserParam; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.NonNull; 7 | import lombok.RequiredArgsConstructor; 8 | 9 | import java.util.Date; 10 | 11 | @Data 12 | @RequiredArgsConstructor 13 | @EqualsAndHashCode(callSuper = true) 14 | public class UserQueryForm extends BaseQueryForm { 15 | @NonNull 16 | private String name; 17 | private Date createdTimeStart; 18 | private Date createdTimeEnd; 19 | } 20 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/test/java/io/github/opensabre/boot/crypt/JasyptTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.crypt; 2 | 3 | import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | public class JasyptTest { 9 | 10 | @Test 11 | public void jasyptTest() { 12 | //加密工具 13 | StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); 14 | //生成秘钥的公钥 15 | encryptor.setPassword("Pa55w0rd@opensabre"); 16 | //加密 17 | String ciphertext = encryptor.decrypt("yLMBDZMhsJziMcceIT1IWw=="); 18 | assertEquals("123456", ciphertext); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/desensitizer/LogBackDesensitizer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | 5 | /** 6 | * logback脱敏器 7 | * 8 | * @author zhoutaoo 9 | */ 10 | public interface LogBackDesensitizer { 11 | /** 12 | * 是否支持脱敏 13 | * 14 | * @param event 日志事件 15 | * @return true/false 支持/不支持 16 | */ 17 | boolean support(ILoggingEvent event); 18 | 19 | /** 20 | * 脱敏接口定义 21 | * 22 | * @param event 事件 23 | * @param originStr 原始字串 24 | * @return 脱敏后的字串 25 | */ 26 | String desensitize(final ILoggingEvent event, String originStr); 27 | } 28 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BRIGHT_YELLOW}${AnsiStyle.BOLD} 2 | ___ _ 3 | / _ \ _ __ ___ _ __ ___ __ _ | |__ _ __ ___ 4 | | | | | | '_ \ / _ \ | '_ \ / __| / _` | | '_ \ | '__| / _ \ 5 | | |_| | | |_) | | __/ | | | | \__ \ | (_| | | |_) | | | | __/ 6 | \___/ | .__/ \___| |_| |_| |___/ \__,_| |_.__/ |_| \___| 7 | |_| 8 | ${AnsiColor.CYAN}${AnsiStyle.BOLD} 9 | :: Java :: (v${java.version}) 10 | :: Spring Boot :: ${spring-boot.formatted-version} 11 | :: Opensabre :: ${opensabre.formatted-version} 12 | :: Application :: (v${git.build.version}) 13 | ${AnsiStyle.NORMAL} -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/openfeign/config/OpensabreLoadBalancerConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.openfeign.config; 2 | 3 | import io.github.opensabre.boot.config.YamlPropertyLoaderFactory; 4 | import org.springframework.boot.autoconfigure.AutoConfiguration; 5 | import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; 6 | import org.springframework.context.annotation.PropertySource; 7 | 8 | /** 9 | * 负载均衡配置 10 | */ 11 | @AutoConfiguration 12 | @LoadBalancerClients(defaultConfiguration = OpensabreLoadBalancerBean.class) 13 | @PropertySource(value = {"classpath:opensabre-rpc.yml"}, encoding = "UTF8", factory = YamlPropertyLoaderFactory.class) 14 | public class OpensabreLoadBalancerConfig { 15 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/OpensabreBootConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | import io.github.opensabre.common.web.exception.DefaultGlobalExceptionHandlerAdvice; 4 | import io.github.opensabre.common.web.rest.RestResponseBodyAdvice; 5 | import org.springframework.boot.autoconfigure.AutoConfiguration; 6 | import org.springframework.context.annotation.Import; 7 | import org.springframework.context.annotation.PropertySource; 8 | 9 | /** 10 | * 初使化全局报文和全局异常配置 11 | */ 12 | @AutoConfiguration 13 | @PropertySource(value = "classpath:opensabre-boot.properties", encoding = "UTF8") 14 | @Import({DefaultGlobalExceptionHandlerAdvice.class, RestResponseBodyAdvice.class}) 15 | public class OpensabreBootConfig { 16 | } 17 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/resources/opensabre-persistence.properties: -------------------------------------------------------------------------------- 1 | # 默认mysql 2 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 3 | # 默认表名 _ 转驼峰 4 | mybatis-plus.configuration.map-underscore-to-camel-case=true 5 | # 逻辑已删除值(默认为 Y) 6 | mybatis-plus.global-config.db-config.logic-delete-value=Y 7 | # 逻辑未删除值(默认为 N) 8 | mybatis-plus.global-config.db-config.logic-not-delete-value=N 9 | #==============mybatis plugs插件默认===============# 10 | # 持久化mybatis分页插件,默认mysql 11 | opensabre.persistence.interceptor.pagination.enabled=true 12 | opensabre.persistence.interceptor.pagination.dbType=mysql 13 | # 持久化mybatis全表扫描插件 14 | opensabre.persistence.interceptor.blockattack.enabled=true 15 | # 持久化mybatis隐患sql插件 16 | opensabre.persistence.interceptor.illegalsql.enabled=false 17 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/resources/opensabre-rpc.yml: -------------------------------------------------------------------------------- 1 | feign: 2 | circuitbreaker: 3 | enabled: true 4 | sentinel: 5 | enabled: true 6 | httpclient: 7 | hc5: 8 | enabled: true 9 | compression: 10 | request: 11 | enabled: true 12 | response: 13 | enabled: true 14 | client: 15 | config: 16 | default: 17 | connectTimeout: 5000 18 | readTimeout: 10000 19 | loggerLevel: basic 20 | spring: 21 | cloud: 22 | sentinel: 23 | enabled: true 24 | datasource: 25 | opensabre: 26 | nacos: 27 | server-addr: ${REGISTER_HOST:localhost}:${REGISTER_PORT:8848} 28 | groupId: DEFAULT_GROUP 29 | dataId: ${spring.application.name}-sentinel.json 30 | rule-type: flow -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rest/strategy/CustomSensitiveStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rest.strategy; 2 | 3 | import io.github.opensabre.boot.sensitive.rule.SensitiveRule; 4 | 5 | /** 6 | * 自定义脱敏策略 7 | */ 8 | public class CustomSensitiveStrategy implements SensitiveStrategy { 9 | 10 | private final SensitiveRule sensitiveRule; 11 | 12 | /** 13 | * @param sensitiveRule 脱敏规则 14 | */ 15 | public CustomSensitiveStrategy(SensitiveRule sensitiveRule) { 16 | this.sensitiveRule = sensitiveRule; 17 | } 18 | 19 | /** 20 | * 脱敏处理 21 | * 22 | * @param str 原字符 23 | * @return 脱敏后的字符 24 | */ 25 | public String desensitizing(SensitiveRule type, String str) { 26 | return sensitiveRule.replace(str); 27 | } 28 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/annotations/Audit.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * 审计日志注解 7 | */ 8 | @Target(ElementType.METHOD) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Documented 11 | public @interface Audit { 12 | 13 | /** 14 | * 操作类型 15 | */ 16 | OperationType operationType(); 17 | 18 | /** 19 | * 操作描述 20 | */ 21 | String description(); 22 | 23 | /** 24 | * 操作模块 25 | */ 26 | String module() default ""; 27 | 28 | /** 29 | * 是否记录请求参数 30 | */ 31 | boolean request() default true; 32 | 33 | /** 34 | * 是否记录响应结果 35 | */ 36 | boolean response() default false; 37 | 38 | /** 39 | * 目标对象关键信息,可支持 spel 40 | */ 41 | String key() default ""; 42 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/entity/form/BaseForm.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.entity.form; 2 | 3 | import io.github.opensabre.common.web.entity.convert.EntityModelConverter; 4 | import io.swagger.v3.oas.annotations.media.Schema; 5 | import lombok.Data; 6 | 7 | import java.io.Serializable; 8 | 9 | @Data 10 | @Schema 11 | public class BaseForm

implements Serializable { 12 | /** 13 | * 用户名 14 | */ 15 | @Schema(title = "用户名", description = "Form提交时操作人的用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "admin") 16 | private String username; 17 | 18 | /** 19 | * Form转化为Po 20 | * 21 | * @param clazz Po类 22 | * @return 返回Po 23 | */ 24 | public P toPo(Class

clazz) { 25 | return EntityModelConverter.getInstance().convert(this, clazz); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/YamlPropertyLoaderFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | import lombok.NonNull; 4 | import org.springframework.boot.env.YamlPropertySourceLoader; 5 | import org.springframework.core.env.PropertySource; 6 | import org.springframework.core.io.support.DefaultPropertySourceFactory; 7 | import org.springframework.core.io.support.EncodedResource; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 加载yaml的Factory 13 | */ 14 | public class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory { 15 | 16 | @Override 17 | public @NonNull PropertySource createPropertySource(String name, EncodedResource resource) throws IOException { 18 | return new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()).get(0); 19 | } 20 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/validator/Mobile.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.validator; 2 | 3 | import jakarta.validation.Constraint; 4 | import jakarta.validation.Payload; 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.Target; 8 | 9 | import static java.lang.annotation.ElementType.*; 10 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 11 | 12 | /** 13 | * 中国手机号校验器 14 | */ 15 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 16 | @Retention(RUNTIME) 17 | @Constraint(validatedBy = MobileValidator.class)//标明由哪个类执行校验逻辑 18 | @Documented 19 | public @interface Mobile { 20 | /** 21 | * 校验失败提示信息 22 | * 23 | * @return 校验失败提示信息 24 | */ 25 | String message() default "手机号格式错误"; 26 | 27 | Class[] groups() default {}; 28 | 29 | Class[] payload() default {}; 30 | } 31 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rest/strategy/DefaultSensitiveStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rest.strategy; 2 | 3 | import io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule; 4 | import io.github.opensabre.boot.sensitive.rule.SensitiveRule; 5 | 6 | import java.util.Arrays; 7 | 8 | import static io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule.values; 9 | 10 | /** 11 | * 默认的脱敏策略 12 | */ 13 | public class DefaultSensitiveStrategy implements SensitiveStrategy { 14 | 15 | /** 16 | * 脱敏处理 17 | * 18 | * @param str 原字符 19 | * @return 脱敏后的字符 20 | */ 21 | public String desensitizing(SensitiveRule type, String str) { 22 | DefaultSensitiveRule sensitiveRule = Arrays.stream(values()).sequential() 23 | .filter(rule -> rule.equals(type)) 24 | .findFirst().orElse(DefaultSensitiveRule.CUSTOM); 25 | return sensitiveRule.replace(str); 26 | } 27 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/validator/MobileValidator.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.validator; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import jakarta.validation.ConstraintValidator; 6 | import jakarta.validation.ConstraintValidatorContext; 7 | import java.util.regex.Pattern; 8 | 9 | /** 10 | * 手机号校验判定类 11 | */ 12 | public class MobileValidator implements ConstraintValidator { 13 | /** 14 | * 中国手机号正则 15 | */ 16 | public static final String REG_MOBILE = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$"; 17 | 18 | @Override 19 | public void initialize(Mobile constraintAnnotation) { 20 | } 21 | 22 | @Override 23 | public boolean isValid(String value, ConstraintValidatorContext context) { 24 | //为空时不进行校验 25 | if (StringUtils.isBlank(value)) 26 | return true; 27 | return Pattern.matches(REG_MOBILE, value); 28 | } 29 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/java/io/github/opensabre/persistence/exception/PersistenceExceptionHandlerAdvice.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.exception; 2 | 3 | import io.github.opensabre.common.core.entity.vo.Result; 4 | import io.github.opensabre.common.core.exception.SystemErrorType; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.core.annotation.Order; 7 | import org.springframework.dao.DuplicateKeyException; 8 | import org.springframework.web.bind.annotation.ExceptionHandler; 9 | import org.springframework.web.bind.annotation.RestControllerAdvice; 10 | 11 | @Slf4j 12 | @Order(100) 13 | @RestControllerAdvice 14 | public class PersistenceExceptionHandlerAdvice { 15 | 16 | @ExceptionHandler(value = {DuplicateKeyException.class}) 17 | public Result duplicateKeyException(DuplicateKeyException ex) { 18 | log.error("primary key duplication exception:{}", ex.getMessage()); 19 | return Result.fail(SystemErrorType.DUPLICATE_PRIMARY_KEY); 20 | } 21 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/desensitizer/AbstractLogBackDesensitizer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | 5 | /** 6 | * 日志脱敏处理器抽象类 7 | * 8 | * @author zhoutaoo 9 | */ 10 | public abstract class AbstractLogBackDesensitizer implements LogBackDesensitizer { 11 | /** 12 | * 如果实现类支持,再执行脱敏的动作 13 | * 14 | * @param event 日志事件 15 | * @param originStr 日志信息 16 | * @return 脱敏后的字符 17 | */ 18 | @Override 19 | public String desensitize(ILoggingEvent event, String originStr) { 20 | if (support(event)) 21 | return desensitizing(event, originStr); 22 | return originStr; 23 | } 24 | 25 | /** 26 | * 脱敏执行 27 | * 28 | * @param event 日志事件 29 | * @param originStr 日志信息 30 | * @return 脱敏后的字符 31 | */ 32 | public abstract String desensitizing(ILoggingEvent event, String originStr); 33 | } 34 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/validator/EnumStringValidator.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.validator; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import jakarta.validation.ConstraintValidator; 6 | import jakarta.validation.ConstraintValidatorContext; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | /** 11 | * 枚举字符串校验注解判定器 12 | */ 13 | public class EnumStringValidator implements ConstraintValidator { 14 | /** 15 | * 枚举字串,被校验对象,包含在内的字串 16 | */ 17 | private List enumStringList; 18 | 19 | @Override 20 | public void initialize(EnumString constraintAnnotation) { 21 | enumStringList = Arrays.asList(constraintAnnotation.value()); 22 | } 23 | 24 | @Override 25 | public boolean isValid(String value, ConstraintValidatorContext context) { 26 | //值为空时不校验 27 | if (StringUtils.isBlank(value)) 28 | return true; 29 | return enumStringList.contains(value); 30 | } 31 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/metadata/OpensabreVersion.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.metadata; 2 | 3 | /** 4 | * 获取opensabre版本信息 5 | */ 6 | public class OpensabreVersion { 7 | /** 8 | * Opensabre版本号环境变量Key 9 | */ 10 | public static final String OPENSABRE_VERSION = "opensabre.version"; 11 | /** 12 | * Opensabre完整版本号环境变量Key 13 | */ 14 | public static final String OPENSABRE_FORMATTED_VERSION = "opensabre.formatted-version"; 15 | /** 16 | * 私有构造方法,不允许初使化实例 17 | */ 18 | private OpensabreVersion() { 19 | } 20 | 21 | /** 22 | * 获取Opensabre版本号 23 | * 24 | * @return 版本号 如:1.x 25 | */ 26 | public static String getVersion() { 27 | return OpensabreVersion.class.getPackage().getImplementationVersion(); 28 | } 29 | 30 | /** 31 | * 获取Opensabre完整版本号 32 | * 33 | * @return 版本号,如:(v1.x.x) 34 | */ 35 | public static String getVersionString() { 36 | return " (v" + getVersion() + ")"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /opensabre-starter-register/src/main/java/io/github/opensabre/register/config/OpensabreNacosDiscoveryConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.register.config; 2 | 3 | import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; 4 | import com.alibaba.cloud.nacos.NacosDiscoveryProperties; 5 | import jakarta.annotation.PostConstruct; 6 | import jakarta.annotation.Resource; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.boot.autoconfigure.AutoConfiguration; 9 | 10 | /** 11 | * 初使化元数据,将元数据注册到注册中心 12 | */ 13 | @Slf4j 14 | @AutoConfiguration 15 | @ConditionalOnNacosDiscoveryEnabled 16 | public class OpensabreNacosDiscoveryConfiguration { 17 | 18 | @Resource 19 | private NacosDiscoveryProperties nacosDiscoveryProperties; 20 | 21 | /** 22 | * 将Opensabre元数据注册到注册中心 23 | */ 24 | @PostConstruct 25 | public void initNacos() { 26 | log.info("Opensabre Metadata Register NacosDiscoveryConfig"); 27 | // 原来的元数据全部不变 28 | nacosDiscoveryProperties.getMetadata().putAll(new Metadata().getMetadata()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/META-INF/spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [ 3 | ], 4 | "properties": [ 5 | { 6 | "name": "opensabre.rest.result.framework.excludes", 7 | "type": "java.lang.String", 8 | "description": "Opensabre Framework Restful Unified Response Packet Format Exclusion Package Name, Multiple Package Names Are Separated By ',' ." 9 | }, 10 | { 11 | "name": "opensabre.rest.result.excludes", 12 | "type": "java.lang.String", 13 | "description": "Opensabre Application Restful Unified Response Packet Format Exclusion Package Name, Multiple Package Names Are Separated By ',' ." 14 | }, 15 | { 16 | "name": "opensabre.sensitive.log.enabled", 17 | "type": "java.lang.Boolean", 18 | "description": "Opensabre Application Sensitive log Enabled, Default is 'true' ." 19 | }, 20 | { 21 | "name": "opensabre.sensitive.log.rules", 22 | "type": "java.lang.String", 23 | "description": "Opensabre Application Sensitive log rules, Default is 'mobile,idCard,phone' ." 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [ 3 | ], 4 | "properties": [ 5 | { 6 | "name": "opensabre.rest.result.framework.excludes", 7 | "type": "java.lang.String", 8 | "description": "Opensabre Framework Restful Unified Response Packet Format Exclusion Package Name, Multiple Package Names Are Separated By ',' ." 9 | }, 10 | { 11 | "name": "opensabre.rest.result.excludes", 12 | "type": "java.lang.String", 13 | "description": "Opensabre Application Restful Unified Response Packet Format Exclusion Package Name, Multiple Package Names Are Separated By ',' ." 14 | }, 15 | { 16 | "name": "opensabre.sensitive.log.enabled", 17 | "type": "java.lang.Boolean", 18 | "description": "Opensabre Application Sensitive log Enabled, Default is 'true' ." 19 | }, 20 | { 21 | "name": "opensabre.sensitive.log.rules", 22 | "type": "java.lang.String", 23 | "description": "Opensabre Application Sensitive log rules, Default is 'mobile,idCard,phone' ." 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/test/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | bus: 4 | trace: 5 | enabled: true 6 | format: 7 | date-time: yyyy-MM-dd HH:mm:ss 8 | date: yyyy-MM-dd 9 | servlet: 10 | multipart: 11 | max-request-size: "2MB" 12 | max-file-size: "2MB" 13 | jackson: 14 | time-zone: GMT+8 15 | date-format: yyyy-MM-dd HH:mm:ss 16 | # 优雅停机 17 | server: 18 | shutdown: graceful 19 | # 配置项加密密钥默认配置 20 | jasypt: 21 | encryptor: 22 | password: ${JASYPT_ENCRYPTOR_PASSWORD:Pa55w0rd@opensabre} 23 | # opensabre framework配置 24 | opensabre: 25 | sensitive: 26 | log: 27 | enabled: true # 日志脱敏开关,默认关闭 28 | rules: mobile,idCard,phone 29 | rest: 30 | result: 31 | framework: 32 | excludes: org.springdoc # 统一报文排除的包名,该包下的rest不使用统一报文,框架内置 33 | excludes: # 统一报文排除的包名,该包下的rest不使用统一报文,应用级配置 34 | # 日志相关 35 | logging: 36 | level: 37 | io.github.opensabre: info 38 | logback: 39 | rollingpolicy: 40 | max-file-size: 1GB 41 | file: 42 | path: logs 43 | config: classpath:logback-spring.xml 44 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/exception/PersistenceExceptionHandlerAdviceTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.exception; 2 | 3 | import io.github.opensabre.common.core.entity.vo.Result; 4 | import io.github.opensabre.common.core.exception.SystemErrorType; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.springframework.dao.DuplicateKeyException; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertEquals; 10 | 11 | class PersistenceExceptionHandlerAdviceTest { 12 | 13 | private PersistenceExceptionHandlerAdvice persistenceExceptionHandlerAdvice; 14 | 15 | @BeforeEach 16 | public void before() { 17 | this.persistenceExceptionHandlerAdvice = new PersistenceExceptionHandlerAdvice(); 18 | } 19 | 20 | @Test 21 | public void testDuplicateKeyException() { 22 | Result result = persistenceExceptionHandlerAdvice.duplicateKeyException(new DuplicateKeyException("主键重复")); 23 | assertEquals(SystemErrorType.DUPLICATE_PRIMARY_KEY.getCode(), result.getCode()); 24 | } 25 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/core/exception/BaseException.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.exception; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class BaseException extends RuntimeException { 7 | /** 8 | * 异常对应的错误类型 9 | */ 10 | private final ErrorType errorType; 11 | 12 | /** 13 | * 默认是系统异常 14 | */ 15 | public BaseException() { 16 | this.errorType = SystemErrorType.SYSTEM_ERROR; 17 | } 18 | 19 | /** 20 | * @param errorType 错误类型 21 | */ 22 | public BaseException(ErrorType errorType) { 23 | this.errorType = errorType; 24 | } 25 | 26 | /** 27 | * @param errorType 错误类型 28 | * @param message 错误信息 29 | */ 30 | public BaseException(ErrorType errorType, String message) { 31 | super(message); 32 | this.errorType = errorType; 33 | } 34 | 35 | /** 36 | * @param errorType 错误类型 37 | * @param message 错误信息 38 | * @param cause 异常 39 | */ 40 | public BaseException(ErrorType errorType, String message, Throwable cause) { 41 | super(message, cause); 42 | this.errorType = errorType; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/event/OpensabreSensitiveDesensitizerProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.event; 2 | 3 | import io.github.opensabre.boot.sensitive.log.desensitizer.LogBackDesensitizer; 4 | import io.github.opensabre.boot.sensitive.log.strategy.DefaultDesensitizerStrategy; 5 | import lombok.NonNull; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.beans.factory.config.BeanPostProcessor; 9 | 10 | import jakarta.annotation.Resource; 11 | 12 | @Slf4j 13 | public class OpensabreSensitiveDesensitizerProcessor implements BeanPostProcessor { 14 | 15 | @Resource 16 | private DefaultDesensitizerStrategy defaultDesensitizerStrategy; 17 | 18 | @Override 19 | public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { 20 | // LogBackDesensitizer的实例自动加载 21 | if (bean instanceof LogBackDesensitizer) { 22 | log.info("postProcessAfterInitialization==> Bean Name: {}", beanName); 23 | defaultDesensitizerStrategy.addDesensitizer((LogBackDesensitizer) bean); 24 | } 25 | return bean; 26 | } 27 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/resources/META-INF/spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [ 3 | ], 4 | "properties": [ 5 | { 6 | "name": "opensabre.persistence.interceptor.pagination.enabled", 7 | "type": "java.lang.Boolean", 8 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, PaginationInnerInterceptor Enabled, Default Is true ." 9 | }, 10 | { 11 | "name": "opensabre.persistence.interceptor.pagination.DbType", 12 | "type": "java.lang.String", 13 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, PaginationInnerInterceptor DbType Default DbType is mysql ." 14 | }, 15 | { 16 | "name": "opensabre.persistence.interceptor.blockattack.enabled", 17 | "type": "java.lang.Boolean", 18 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, BlockAttackInnerInterceptor Default Is true ." 19 | }, 20 | { 21 | "name": "opensabre.persistence.interceptor.illegalsql.enabled", 22 | "type": "java.lang.Boolean", 23 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, IllegalSQLInnerInterceptor Default Is false ." 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/java/io/github/opensabre/persistence/entity/param/BaseParam.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.param; 2 | 3 | import io.swagger.v3.oas.annotations.media.Schema; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | import java.io.Serializable; 8 | import java.util.Date; 9 | 10 | /** 11 | * Created by zhoutaoo on 2018/6/1. 12 | * 13 | * @author zhoutaoo 14 | */ 15 | @Data 16 | @NoArgsConstructor 17 | public class BaseParam implements Serializable { 18 | @Schema(title = "开始时间", description = "查询条件创建记录的开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example="2020-05-06 12:23:23") 19 | private Date createdTimeStart; 20 | @Schema(title = "结束时间", description = "查询条件创建记录的结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example="2020-05-12 12:23:23") 21 | private Date createdTimeEnd; 22 | 23 | // public QueryWrapper build() { 24 | // QueryWrapper queryWrapper = new QueryWrapper<>(); 25 | // queryWrapper.ge(null != this.createdTimeStart, "created_time", this.createdTimeStart) 26 | // .le(null != this.createdTimeEnd, "created_time", this.createdTimeEnd); 27 | // return queryWrapper; 28 | // } 29 | } 30 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/resources/META-INF/additional-spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "groups": [ 3 | ], 4 | "properties": [ 5 | { 6 | "name": "opensabre.persistence.interceptor.pagination.enabled", 7 | "type": "java.lang.Boolean", 8 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, PaginationInnerInterceptor Enabled, Default Is true ." 9 | }, 10 | { 11 | "name": "opensabre.persistence.interceptor.pagination.DbType", 12 | "type": "java.lang.String", 13 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, PaginationInnerInterceptor DbType Default DbType is mysql ." 14 | }, 15 | { 16 | "name": "opensabre.persistence.interceptor.blockattack.enabled", 17 | "type": "java.lang.Boolean", 18 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, BlockAttackInnerInterceptor Default Is true ." 19 | }, 20 | { 21 | "name": "opensabre.persistence.interceptor.illegalsql.enabled", 22 | "type": "java.lang.Boolean", 23 | "description": "Opensabre Framework Persistence Mybatis Plugs Interceptor, IllegalSQLInnerInterceptor Default Is false ." 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/entity/convert/EntityModelConverter.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.entity.convert; 2 | 3 | import lombok.SneakyThrows; 4 | import org.springframework.beans.BeanUtils; 5 | 6 | /** 7 | * 对象转换工具类 8 | * @author zhoutaoo 9 | */ 10 | public class EntityModelConverter implements EntityConverter { 11 | private EntityModelConverter() { 12 | } 13 | 14 | /** 15 | * 返回实例 16 | * 17 | * @return 单例 18 | */ 19 | public static EntityModelConverter getInstance() { 20 | return SingletonHolder.sInstance; 21 | } 22 | 23 | /** 24 | * 静态内部类单例模式 25 | * 单例初使化 26 | */ 27 | private static class SingletonHolder { 28 | private static final EntityModelConverter sInstance = new EntityModelConverter(); 29 | } 30 | 31 | /** 32 | * 将源对象转换为 目标对象 33 | * 34 | * @param source 源对象 35 | * @param targetClass 目标对象类型 36 | * @return 目标对象 37 | */ 38 | @SneakyThrows 39 | public P convert(F source, Class

targetClass) { 40 | P target = targetClass.getDeclaredConstructor().newInstance(); 41 | BeanUtils.copyProperties(source, target); 42 | return target; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/event/OpensabreStartedEventHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.event; 2 | 3 | import io.github.opensabre.boot.rest.MappingInfoHandler; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.boot.context.event.ApplicationReadyEvent; 6 | import org.springframework.context.ApplicationContext; 7 | import org.springframework.context.ApplicationListener; 8 | 9 | import jakarta.annotation.Resource; 10 | 11 | /** 12 | * springboot应用启动完成后,发送Rest注册事件 13 | */ 14 | @Slf4j 15 | public class OpensabreStartedEventHandler implements ApplicationListener { 16 | /** 17 | * spring上下文 18 | */ 19 | @Resource 20 | private ApplicationContext context; 21 | /** 22 | * Rest信息获取处对象 23 | */ 24 | @Resource 25 | MappingInfoHandler mappingInfoHandler; 26 | 27 | @Override 28 | public void onApplicationEvent(ApplicationReadyEvent event) { 29 | log.info("ApplicationReadyEvent received"); 30 | mappingInfoHandler.getMappingInfo().forEach(mappingInfo -> { 31 | context.publishEvent(new MappingRegisteredEvent(mappingInfo)); 32 | log.info("Mapping Registered :{}", mappingInfo); 33 | }); 34 | } 35 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/metadata/OpensabreCloud.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.metadata; 2 | 3 | /** 4 | * 获取opensabre cloud相关信息,用于支持云原生多活等 5 | */ 6 | public class OpensabreCloud { 7 | /** 8 | * Opensabre云部署AZ环境变量Key 9 | */ 10 | public static final String OPENSABRE_CLOUD_AZ = "opensabre.cloud.az"; 11 | /** 12 | * Opensabre云部署REGION环境变量Key 13 | */ 14 | public static final String OPENSABRE_CLOUD_REGION = "opensabre.cloud.region"; 15 | /** 16 | * 云部署时环境变量,代表可用区 17 | */ 18 | public static final String ENV_CLOUD_AZ = "CLOUD_AZ"; 19 | /** 20 | * 云部署时环境变量,代表地区 21 | */ 22 | public static final String ENV_CLOUD_REGION = "CLOUD_REGION"; 23 | 24 | /** 25 | * 私有构造方法,不允许初使化实例 26 | */ 27 | private OpensabreCloud() { 28 | } 29 | 30 | /** 31 | * 从环境变量中获取Opensabre部署AZ信息 32 | * 33 | * @return 云环境AZ可用区代号 34 | */ 35 | public static String getCloudAz() { 36 | return System.getenv(ENV_CLOUD_AZ); 37 | } 38 | 39 | /** 40 | * 从环境变量中获取Opensabre部署REGION信息 41 | * 42 | * @return 云环境REGION地域 43 | */ 44 | public static String getCloudRegion() { 45 | return System.getenv(ENV_CLOUD_REGION); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /opensabre-starter-register/src/main/java/io/github/opensabre/register/config/Metadata.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.register.config; 2 | 3 | import io.github.opensabre.boot.metadata.OpensabreCloud; 4 | import io.github.opensabre.boot.metadata.OpensabreVersion; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | import static io.github.opensabre.boot.metadata.OpensabreCloud.OPENSABRE_CLOUD_AZ; 10 | import static io.github.opensabre.boot.metadata.OpensabreCloud.OPENSABRE_CLOUD_REGION; 11 | import static io.github.opensabre.boot.metadata.OpensabreVersion.OPENSABRE_VERSION; 12 | 13 | /** 14 | * 应用元数据类 15 | */ 16 | public class Metadata { 17 | 18 | /** 19 | * 元数据存储容器 20 | */ 21 | private final Map metadata = new HashMap<>(); 22 | 23 | /** 24 | * 构建方法内初使化元数据 25 | */ 26 | public Metadata() { 27 | metadata.put(OPENSABRE_VERSION, OpensabreVersion.getVersion()); 28 | metadata.put(OPENSABRE_CLOUD_AZ, OpensabreCloud.getCloudAz()); 29 | metadata.put(OPENSABRE_CLOUD_REGION, OpensabreCloud.getCloudRegion()); 30 | } 31 | 32 | /** 33 | * 获取全部元数据 34 | * 35 | * @return all metadata 36 | */ 37 | public Map getMetadata() { 38 | return metadata; 39 | } 40 | } -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # 相当于脚本用途的一个声明 2 | name: Maven Central Repo Deployment 3 | # 触发脚本的事件 这里为发布release之后触发 4 | on: 5 | release: 6 | types: [released] 7 | # 定义一个发行任务 8 | jobs: 9 | publish: 10 | # 任务运行的环境 11 | runs-on: ubuntu-latest 12 | # 任务的步骤 13 | steps: 14 | # 1. 声明 checkout 仓库代码到工作区 15 | - name: Checkout Git Repo 16 | uses: actions/checkout@v2 17 | # 2. 安装Java 环境 这里会用到的参数就是 Git Action secrets中配置的, 18 | # 取值要在key前面加 secrets. 19 | - name: Set up Maven Central Repo 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 17 23 | server-id: sonatype-nexus-staging 24 | server-username: ${{ secrets.OSSRH_USER }} 25 | server-password: ${{ secrets.OSSRH_PASSWORD }} 26 | gpg-passphrase: ${{ secrets.GPG_PASSWORD }} 27 | # 3. 发布到Maven中央仓库 28 | - name: Publish to Maven Central Repo 29 | # 这里用到了其他人写的action脚本,详细可以去看他的文档。 30 | uses: samuelmeuli/action-maven-publish@v1 31 | with: 32 | server_id: ossrh 33 | gpg_private_key: ${{ secrets.GPG_SECRET }} 34 | gpg_passphrase: ${{ secrets.GPG_PASSWORD }} 35 | nexus_username: ${{ secrets.OSSRH_USER }} 36 | nexus_password: ${{ secrets.OSSRH_PASSWORD }} -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/openfeign/config/OpensabreLoadBalancerBean.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.openfeign.config; 2 | 3 | import io.github.opensabre.rpc.loadbalance.OpensabreLoadBalancer; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.cloud.client.ServiceInstance; 6 | import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; 7 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; 8 | import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.core.env.Environment; 11 | 12 | public class OpensabreLoadBalancerBean { 13 | @Bean 14 | @ConditionalOnMissingBean 15 | public ReactorLoadBalancer reactorServiceInstanceLoadBalancer(Environment environment, 16 | LoadBalancerClientFactory loadBalancerClientFactory) { 17 | String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); 18 | return new OpensabreLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name); 19 | } 20 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/annotations/Desensitization.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.annotations; 2 | 3 | import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; 4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 5 | import io.github.opensabre.boot.sensitive.rest.DesensitizationSerialize; 6 | import io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule; 7 | 8 | import java.lang.annotation.ElementType; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Target(ElementType.FIELD) 15 | @JacksonAnnotationsInside 16 | @JsonSerialize(using = DesensitizationSerialize.class) 17 | public @interface Desensitization { 18 | 19 | /** 20 | * 脱敏数据类型,只有在CUSTOM的时候,retainPrefixCount和retainSuffixCount生效 21 | * 22 | * @return 脱敏器类型 type 23 | */ 24 | DefaultSensitiveRule type() default DefaultSensitiveRule.CUSTOM; 25 | 26 | /** 27 | * @return 保留前缀个数 28 | */ 29 | int retainPrefixCount() default 0; 30 | 31 | /** 32 | * @return 保留后缀个数 33 | */ 34 | int retainSuffixCount() default 0; 35 | 36 | /** 37 | * @return 掩码符号 38 | */ 39 | char replaceChar() default '*'; 40 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/test/java/io/github/opensabre/boot/sensitive/log/desensitizer/NameLogBackDesensitizerTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.LoggingEvent; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | class NameLogBackDesensitizerTest { 9 | 10 | @Test 11 | void desensitizing() { 12 | NameLogBackDesensitizer nameLogBackDesensitizer = new NameLogBackDesensitizer(); 13 | LoggingEvent loggingEvent = new LoggingEvent(); 14 | assertEquals("name : 张*", nameLogBackDesensitizer.desensitizing(loggingEvent, "name : 张三")); 15 | assertEquals("name: 张**", nameLogBackDesensitizer.desensitizing(loggingEvent, "name: 张三小")); 16 | assertEquals("name is 张**", nameLogBackDesensitizer.desensitizing(loggingEvent, "name is 张三小")); 17 | assertEquals("姓名:李**", nameLogBackDesensitizer.desensitizing(loggingEvent, "姓名:李四大")); 18 | assertEquals("姓名=李**", nameLogBackDesensitizer.desensitizing(loggingEvent, "姓名=李四大")); 19 | assertEquals("姓名>>李*****", nameLogBackDesensitizer.desensitizing(loggingEvent, "姓名>>李四大买买提")); 20 | assertEquals("姓名<李*****提", nameLogBackDesensitizer.desensitizing(loggingEvent, "姓名<李四大买买提提")); 21 | } 22 | } -------------------------------------------------------------------------------- /opensabre-starter-register/src/main/java/io/github/opensabre/register/config/OpensabreNacosProjectConfiguration.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.register.config; 2 | 3 | import com.alibaba.cloud.nacos.ConditionalOnNacosDiscoveryEnabled; 4 | import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClientConfiguration; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.boot.autoconfigure.AutoConfiguration; 9 | import org.springframework.boot.autoconfigure.AutoConfigureBefore; 10 | import org.springframework.context.EnvironmentAware; 11 | import org.springframework.core.env.Environment; 12 | 13 | /** 14 | * 初使nacos的projectName,解决默认的订阅者应用名为空的问题 15 | */ 16 | @Slf4j 17 | @AutoConfiguration 18 | @ConditionalOnNacosDiscoveryEnabled 19 | @AutoConfigureBefore({NacosDiscoveryClientConfiguration.class}) 20 | public class OpensabreNacosProjectConfiguration implements EnvironmentAware { 21 | 22 | @Value("${spring.application.name}") 23 | private String applicationName; 24 | 25 | @Override 26 | public void setEnvironment(Environment environment) { 27 | if (StringUtils.isBlank(System.getProperty("project.name"))) { 28 | System.setProperty("project.name", applicationName); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /opensabre-web/src/test/java/io/github/opensabre/common/web/interceptor/UserInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.interceptor; 2 | 3 | import io.github.opensabre.common.core.util.UserContextHolder; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.mock.web.MockHttpServletRequest; 6 | import org.springframework.mock.web.MockHttpServletResponse; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public class UserInterceptorTest { 12 | @Test 13 | public void preHandle_当未设置token_user_那么正常处理下一个handle() throws Exception { 14 | UserInterceptor userInterceptor = new UserInterceptor(); 15 | assertTrue(userInterceptor.preHandle(new MockHttpServletRequest(), new MockHttpServletResponse(), new Object())); 16 | } 17 | 18 | @Test 19 | public void preHandle_当设置token的username_那么username可以在线程中拿出来用() throws Exception { 20 | UserInterceptor userInterceptor = new UserInterceptor(); 21 | MockHttpServletRequest request = new MockHttpServletRequest(); 22 | request.addHeader("x-client-token-user", "{\"user_name\":\"zhangsan\"}"); 23 | userInterceptor.preHandle(request, new MockHttpServletResponse(), new Object()); 24 | assertEquals(UserContextHolder.getInstance().getUsername(), "zhangsan"); 25 | } 26 | } -------------------------------------------------------------------------------- /opensabre-starter-webmvc/src/test/java/io/github/opensabre/webmvc/interceptor/UserInterceptorTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.webmvc.interceptor; 2 | 3 | import io.github.opensabre.common.core.util.UserContextHolder; 4 | import org.junit.jupiter.api.Test; 5 | import org.springframework.mock.web.MockHttpServletRequest; 6 | import org.springframework.mock.web.MockHttpServletResponse; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public class UserInterceptorTest { 12 | @Test 13 | public void preHandle_当未设置token_user_那么正常处理下一个handle() throws Exception { 14 | UserInterceptor userInterceptor = new UserInterceptor(); 15 | assertTrue(userInterceptor.preHandle(new MockHttpServletRequest(), new MockHttpServletResponse(), new Object())); 16 | } 17 | 18 | @Test 19 | public void preHandle_当设置token的username_那么username可以在线程中拿出来用() throws Exception { 20 | UserInterceptor userInterceptor = new UserInterceptor(); 21 | MockHttpServletRequest request = new MockHttpServletRequest(); 22 | request.addHeader("x-client-token-user", "{\"user_name\":\"zhangsan\"}"); 23 | userInterceptor.preHandle(request, new MockHttpServletResponse(), new Object()); 24 | assertEquals(UserContextHolder.getInstance().getUsername(), "zhangsan"); 25 | } 26 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/strategy/DefaultDesensitizerStrategy.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.strategy; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | import com.google.common.collect.Sets; 5 | import io.github.opensabre.boot.sensitive.log.desensitizer.LogBackDesensitizer; 6 | import io.github.opensabre.boot.sensitive.log.desensitizer.PasswordLogBackDesensitizer; 7 | 8 | import java.util.Set; 9 | import java.util.concurrent.atomic.AtomicReference; 10 | 11 | /** 12 | * 默认脱敏策略 13 | * 可支持多个脱敏器,循环使用全部脱敏器处理一次 14 | */ 15 | public class DefaultDesensitizerStrategy implements DesensitizerStrategy { 16 | /** 17 | * 默认的脱敏器 18 | */ 19 | private final Set desensitizers = Sets.newHashSet(new PasswordLogBackDesensitizer()); 20 | 21 | @Override 22 | public String desensitizing(ILoggingEvent event) { 23 | AtomicReference message = new AtomicReference<>(event.getFormattedMessage()); 24 | desensitizers.forEach(desensitizer -> { 25 | message.set(desensitizer.desensitize(event, message.get())); 26 | }); 27 | return message.get(); 28 | } 29 | 30 | /** 31 | * 追回脱敏器 32 | * 33 | * @param logBackDesensitizer 脱敏器 34 | */ 35 | public void addDesensitizer(LogBackDesensitizer logBackDesensitizer) { 36 | desensitizers.add(logBackDesensitizer); 37 | } 38 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/java/io/github/opensabre/persistence/handler/PoMetaObjectHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.handler; 2 | 3 | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; 4 | import io.github.opensabre.common.core.util.UserContextHolder; 5 | import io.github.opensabre.persistence.entity.po.BasePo; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.apache.ibatis.reflection.MetaObject; 9 | 10 | import java.time.Instant; 11 | import java.util.Date; 12 | 13 | @Slf4j 14 | public class PoMetaObjectHandler implements MetaObjectHandler { 15 | /** 16 | * 获取当前交易的用户,为空返回默认system 17 | * 18 | * @return CurrentUsername 19 | */ 20 | private String getCurrentUsername() { 21 | return StringUtils.defaultIfBlank(UserContextHolder.getInstance().getUsername(), BasePo.DEFAULT_USERNAME); 22 | } 23 | 24 | @Override 25 | public void insertFill(MetaObject metaObject) { 26 | this.strictInsertFill(metaObject, "createdBy", String.class, getCurrentUsername()); 27 | this.strictInsertFill(metaObject, "createdTime", Date.class, Date.from(Instant.now())); 28 | this.updateFill(metaObject); 29 | } 30 | 31 | @Override 32 | public void updateFill(MetaObject metaObject) { 33 | this.strictUpdateFill(metaObject, "updatedBy", String.class, getCurrentUsername()); 34 | this.strictUpdateFill(metaObject, "updatedTime", Date.class, Date.from(Instant.now())); 35 | } 36 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/validator/EnumString.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.validator; 2 | 3 | import jakarta.validation.Constraint; 4 | import jakarta.validation.Payload; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.Repeatable; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.Target; 10 | 11 | import static java.lang.annotation.ElementType.*; 12 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 13 | 14 | /** 15 | * 枚举字符串校验器 16 | * 字段值只能为 value数组中的值 17 | */ 18 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 19 | @Retention(RUNTIME) 20 | @Repeatable(EnumString.List.class) 21 | @Documented 22 | @Constraint(validatedBy = EnumStringValidator.class)//标明由哪个类执行校验逻辑 23 | public @interface EnumString { 24 | /** 25 | * 校验失败默认的提示信息 26 | * 27 | * @return 提示词 28 | */ 29 | String message() default "类型只能为 value 列表内的值"; 30 | 31 | Class[] groups() default {}; 32 | 33 | Class[] payload() default {}; 34 | 35 | /** 36 | * @return data must in this value array 37 | */ 38 | String[] value(); 39 | 40 | /** 41 | * Defines several {@link EnumString} annotations on the same element. 42 | * 43 | * @see EnumString 44 | */ 45 | @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) 46 | @Retention(RUNTIME) 47 | @Documented 48 | @interface List { 49 | EnumString[] value(); 50 | } 51 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/entity/AuditInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.entity; 2 | 3 | import lombok.*; 4 | 5 | import java.time.LocalDateTime; 6 | 7 | import io.github.opensabre.boot.annotations.OperationType; 8 | 9 | /** 10 | * 审计日志实体 11 | */ 12 | @Data 13 | @Builder 14 | @NoArgsConstructor 15 | @AllArgsConstructor 16 | public class AuditInfo { 17 | 18 | /** 19 | * 操作类型 20 | */ 21 | private OperationType operationType; 22 | 23 | /** 24 | * 操作时间 25 | */ 26 | private LocalDateTime operationTime; 27 | 28 | /** 29 | * 操作人用户名 30 | */ 31 | private String operatorUsername; 32 | 33 | /** 34 | * 操作描述 35 | */ 36 | private String description; 37 | 38 | /** 39 | * 操作模块 40 | */ 41 | private String module; 42 | 43 | /** 44 | * 操作IP地址 45 | */ 46 | private String clientIp; 47 | 48 | /** 49 | * 用户代理 50 | */ 51 | private String userAgent; 52 | 53 | /** 54 | * 请求方法 55 | */ 56 | private String requestMethod; 57 | 58 | /** 59 | * 请求URL 60 | */ 61 | private String requestUrl; 62 | 63 | /** 64 | * 请求参数 65 | */ 66 | private String request; 67 | 68 | /** 69 | * 操作结果 70 | */ 71 | private String response; 72 | 73 | /** 74 | * 错误信息 75 | */ 76 | private String errorMessage; 77 | 78 | /** 79 | * 执行时间(毫秒) 80 | */ 81 | private Long executionTime; 82 | 83 | /** 84 | * 操作目标key 85 | */ 86 | private String targetKey; 87 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/java/io/github/opensabre/persistence/entity/form/BaseQueryForm.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.form; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import io.github.opensabre.common.web.entity.form.BaseForm; 5 | import io.github.opensabre.common.web.entity.convert.EntityModelConverter; 6 | import io.github.opensabre.persistence.entity.param.BaseParam; 7 | import io.swagger.v3.oas.annotations.media.Schema; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | 11 | @Data 12 | @Schema 13 | @EqualsAndHashCode(callSuper = true) 14 | public class BaseQueryForm

extends BaseForm { 15 | /** 16 | * 分页查询的参数,当前页数 17 | */ 18 | @Schema(title = "当前页数", description = "分页查询的参数,当前页数", requiredMode = Schema.RequiredMode.REQUIRED, defaultValue = "1") 19 | private long current = 1; 20 | /** 21 | * 分页查询的参数,当前页面每页显示的数量 22 | */ 23 | @Schema(title = "每页数量", description = "分页查询的参数,当前页面每页显示的数量", requiredMode = Schema.RequiredMode.REQUIRED, defaultValue = "10") 24 | private long size = 10; 25 | 26 | /** 27 | * QueryForm转化为Param 28 | * 29 | * @param clazz Param类 30 | * @return 返回Param 31 | */ 32 | public P toParam(Class

clazz) { 33 | return EntityModelConverter.getInstance().convert(this, clazz); 34 | } 35 | 36 | /** 37 | * 从form中获取page参数,用于分页查询参数 38 | * 39 | * @return 返回分页的page 40 | */ 41 | public Page

getPage() { 42 | return new Page<>(this.getCurrent(), this.getSize()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /opensabre-web/src/test/java/io/github/opensabre/common/core/exception/BaseExceptionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.exception; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | class BaseExceptionTest { 8 | @Test 9 | void testNewBaseException() { 10 | assertEquals(new BaseException().getErrorType().getCode(), "-1"); 11 | } 12 | 13 | @Test 14 | void testNewBaseExceptionWithErrorType() { 15 | BaseException baseException = new BaseException(SystemErrorType.ARGUMENT_NOT_VALID); 16 | assertEquals(baseException.getErrorType().getCode(), SystemErrorType.ARGUMENT_NOT_VALID.getCode()); 17 | } 18 | 19 | @Test 20 | void testNewBaseExceptionWithErrorTypeAndMessage() { 21 | BaseException baseException = new BaseException(SystemErrorType.ARGUMENT_NOT_VALID, "无效参数"); 22 | assertEquals(baseException.getErrorType().getCode(), SystemErrorType.ARGUMENT_NOT_VALID.getCode()); 23 | assertEquals(baseException.getErrorType().getMesg(), "请求参数校验不通过"); 24 | assertEquals(baseException.getMessage(), "无效参数"); 25 | 26 | } 27 | 28 | @Test 29 | void testNewBaseExceptionWithErrorTypeAndMessageAndThrowable() { 30 | BaseException baseException = new BaseException(SystemErrorType.ARGUMENT_NOT_VALID, "无效参数", new RuntimeException()); 31 | assertEquals(baseException.getErrorType().getCode(), SystemErrorType.ARGUMENT_NOT_VALID.getCode()); 32 | assertEquals(baseException.getErrorType().getMesg(), "请求参数校验不通过"); 33 | assertEquals(baseException.getMessage(), "无效参数"); 34 | } 35 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rule/SensitiveRule.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rule; 2 | 3 | 4 | import cn.hutool.core.text.CharSequenceUtil; 5 | 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * 脱敏规则 10 | * 11 | * @author zhoutaoo 12 | */ 13 | public interface SensitiveRule { 14 | 15 | /** 16 | * 类别 17 | * 如: 类别[电话],就包含mobile phone telephone等 18 | * 19 | * @return 保留前缀个数 20 | */ 21 | String category(); 22 | 23 | /** 24 | * 正则表达式 25 | * 26 | * @return 脱敏字段的正则 27 | */ 28 | Pattern pattern(); 29 | 30 | /** 31 | * 保留前缀个数 (需满足 大于等于 0个) 32 | *

33 | * 如: 538261, 保留前缀个数为2的话, 那么就是 53 34 | * 35 | * @return 保留前缀个数 36 | */ 37 | int retainPrefixCount(); 38 | 39 | /** 40 | * 保留后缀个数 (需满足 大于等于 0个) 41 | *

42 | * 如: 123456, 保留后缀个数为2的话, 那么就是 56 43 | * 44 | * @return 保留后缀个数 45 | */ 46 | int retainSuffixCount(); 47 | 48 | /** 49 | * 用于替代明文的 密文字符 50 | *

51 | * 如: 对123456使用*进行前2后2的脱敏, 那么就是 12**56 52 | * 53 | * @return 用于替代明文的 密文字符,默认* 54 | */ 55 | default char replaceChar() { 56 | return '*'; 57 | } 58 | 59 | /** 60 | * 原始字符串 替换 61 | * 62 | * @param originStr 原始字符串 63 | * @return 替换后的字符串 64 | */ 65 | default String replace(String originStr) { 66 | return CharSequenceUtil.replace(originStr, 67 | this.retainPrefixCount(), 68 | originStr.length() - this.retainSuffixCount(), 69 | this.replaceChar()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/openfeign/interceptor/FeignHeaderInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.openfeign.interceptor; 2 | 3 | import com.google.common.collect.Maps; 4 | import feign.RequestInterceptor; 5 | import feign.RequestTemplate; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.web.context.request.RequestContextHolder; 8 | import org.springframework.web.context.request.ServletRequestAttributes; 9 | 10 | import jakarta.servlet.http.HttpServletRequest; 11 | import java.util.Enumeration; 12 | import java.util.Map; 13 | 14 | /** 15 | * spring cloud feign传递header 16 | * 17 | * @author zhoutaoo 18 | */ 19 | @Component 20 | public class FeignHeaderInterceptor implements RequestInterceptor { 21 | 22 | /** 23 | * 获取request header 放入远程template中 24 | */ 25 | @Override 26 | public void apply(RequestTemplate template) { 27 | getHeaders().forEach(template::header); 28 | } 29 | 30 | /** 31 | * 获取 request 中的所有的 header 值 32 | * 33 | * @return header map 34 | */ 35 | private Map getHeaders() { 36 | HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); 37 | Map map = Maps.newHashMap(); 38 | Enumeration headerNames = request.getHeaderNames(); 39 | while (headerNames.hasMoreElements()) { 40 | String key = headerNames.nextElement(); 41 | String value = request.getHeader(key); 42 | map.put(key, value); 43 | } 44 | return map; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/core/exception/SystemErrorType.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.exception; 2 | 3 | import lombok.Getter; 4 | 5 | /** 6 | * 系统内置错误类型 7 | */ 8 | @Getter 9 | public enum SystemErrorType implements ErrorType { 10 | /** 11 | * 未预料异常时均处理为该类型 12 | */ 13 | SYSTEM_ERROR("-1", "系统异常"), 14 | /** 15 | * 系统繁忙,限流时 16 | */ 17 | SYSTEM_BUSY("000001", "系统繁忙,请稍候再试"), 18 | /** 19 | * 未找到该资源 20 | */ 21 | RESOURCE_NOT_FOUND("000404", "资源未找到"), 22 | /** 23 | * 网关转发时未找到该服务 24 | */ 25 | GATEWAY_NOT_FOUND_SERVICE("010404", "服务未找到"), 26 | /** 27 | * 网关发生异常 28 | */ 29 | GATEWAY_ERROR("010500", "网关异常"), 30 | /** 31 | * 网关调用后端server超时 32 | */ 33 | GATEWAY_CONNECT_TIME_OUT("010002", "网关超时"), 34 | /** 35 | * Form表单字段校验不通过 36 | */ 37 | ARGUMENT_NOT_VALID("020000", "请求参数校验不通过"), 38 | /** 39 | * Rest不支持的方法 40 | */ 41 | METHOD_NOT_SUPPORTED("020001", "请求方法不支持"), 42 | /** 43 | * 无效Token 44 | */ 45 | INVALID_TOKEN("021001", "无效token"), 46 | /** 47 | * 文件上传时,超过设定大小 48 | */ 49 | UPLOAD_FILE_SIZE_LIMIT("020010", "上传文件大小超过限制"), 50 | /** 51 | * DB处理时,唯一键冲突时返回 52 | */ 53 | DUPLICATE_PRIMARY_KEY("030000", "唯一键冲突"); 54 | 55 | /** 56 | * 错误类型码 57 | */ 58 | private final String code; 59 | /** 60 | * 错误类型描述信息 61 | */ 62 | private final String mesg; 63 | 64 | /** 65 | * 构建函数 66 | * 67 | * @param code 错误代码 68 | * @param mesg 错误提示信息 69 | */ 70 | SystemErrorType(String code, String mesg) { 71 | this.code = code; 72 | this.mesg = mesg; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/test/java/io/github/opensabre/persistence/entity/BaseEntityTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity; 2 | 3 | import io.github.opensabre.persistence.entity.form.UserForm; 4 | import io.github.opensabre.persistence.entity.form.UserQueryForm; 5 | import io.github.opensabre.persistence.entity.param.UserParam; 6 | import io.github.opensabre.persistence.entity.po.User; 7 | import io.github.opensabre.persistence.entity.vo.UserVo; 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.Test; 10 | 11 | import java.util.Date; 12 | 13 | class BaseEntityTest { 14 | @Test 15 | public void testQueryFormToParam() { 16 | UserQueryForm userQueryForm = new UserQueryForm("ZhangSan"); 17 | userQueryForm.setCreatedTimeEnd(new Date()); 18 | userQueryForm.setCreatedTimeStart(new Date()); 19 | UserParam userParam = userQueryForm.toParam(UserParam.class); 20 | Assertions.assertEquals("ZhangSan", userParam.getName()); 21 | } 22 | 23 | @Test 24 | public void testFormToPo() { 25 | UserForm userForm = new UserForm("ZhangSan"); 26 | userForm.setAge(12); 27 | userForm.setSex("F"); 28 | userForm.setBirthday(new Date()); 29 | User user = userForm.toPo(User.class); 30 | Assertions.assertEquals("ZhangSan", user.getName()); 31 | } 32 | 33 | @Test 34 | public void testPoToVo() { 35 | User user = new User("ZhangSan"); 36 | user.setAge(12); 37 | user.setSex("F"); 38 | user.setBirthday(new Date()); 39 | user.setId("12"); 40 | user.setCreatedTime(new Date()); 41 | UserVo userVo = user.toVo(UserVo.class); 42 | Assertions.assertEquals("ZhangSan", userVo.getName()); 43 | } 44 | } -------------------------------------------------------------------------------- /opensabre-starter-eda/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-eda 14 | jar 15 | 16 | opensabre-starter-eda 17 | Opensabre Boot Eda 18 | 19 | 20 | 21 | 22 | io.github.opensabre 23 | opensabre-base-dependencies 24 | ${revision} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-bus-amqp 36 | 37 | 38 | 39 | org.projectlombok 40 | lombok 41 | 42 | 43 | io.github.opensabre 44 | opensabre-starter-boot 45 | 46 | 47 | -------------------------------------------------------------------------------- /opensabre-starter-config/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-config 14 | jar 15 | 16 | opensabre-starter-config 17 | Opensabre Boot Config 18 | 19 | 20 | 21 | 22 | io.github.opensabre 23 | opensabre-base-dependencies 24 | ${revision} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.springframework.cloud 35 | spring-cloud-starter-bootstrap 36 | 37 | 38 | 39 | com.alibaba.cloud 40 | spring-cloud-starter-alibaba-nacos-config 41 | 42 | 43 | io.github.opensabre 44 | opensabre-starter-boot 45 | 46 | 47 | -------------------------------------------------------------------------------- /opensabre-web/src/test/java/io/github/opensabre/common/core/util/UserContextHolderTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.util; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.HashMap; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertNotNull; 9 | 10 | class UserContextHolderTest { 11 | 12 | @Test 13 | void getInstance() { 14 | assertNotNull(UserContextHolder.getInstance()); 15 | //单例,两次获取的同一个实例 16 | assertEquals(UserContextHolder.getInstance(), UserContextHolder.getInstance()); 17 | } 18 | 19 | @Test 20 | void setContext() { 21 | UserContextHolder.getInstance().setContext(new HashMap<>()); 22 | } 23 | 24 | @Test 25 | void getContext() { 26 | HashMap map = new HashMap<>(); 27 | UserContextHolder.getInstance().setContext(map); 28 | assertEquals(UserContextHolder.getInstance().getContext(), UserContextHolder.getInstance().getContext()); 29 | } 30 | 31 | @Test 32 | void getContextNotSet() { 33 | assertEquals(0, UserContextHolder.getInstance().getContext().size()); 34 | } 35 | 36 | @Test 37 | void getUsername() { 38 | HashMap map = new HashMap<>(); 39 | map.put(UserContextHolder.getInstance().KEY_USERNAME, "zhangsan"); 40 | UserContextHolder.getInstance().setContext(map); 41 | assertEquals(UserContextHolder.getInstance().getUsername(), "zhangsan"); 42 | } 43 | 44 | @Test 45 | void clear() { 46 | HashMap map = new HashMap<>(); 47 | map.put(UserContextHolder.getInstance().KEY_USERNAME, "zhangsan"); 48 | UserContextHolder.getInstance().setContext(map); 49 | UserContextHolder.getInstance().clear(); 50 | assertEquals(0, UserContextHolder.getInstance().getContext().size()); 51 | } 52 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/OpensabreSensitiveConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | import io.github.opensabre.boot.event.OpensabreSensitiveDesensitizerProcessor; 4 | import io.github.opensabre.boot.sensitive.log.LogBackCoreConverter; 5 | import io.github.opensabre.boot.sensitive.log.desensitizer.LogBackDesensitizer; 6 | import io.github.opensabre.boot.sensitive.log.desensitizer.RegxLogBackDesensitizer; 7 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 8 | import org.springframework.beans.factory.config.BeanPostProcessor; 9 | import org.springframework.boot.autoconfigure.AutoConfiguration; 10 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 11 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 12 | import org.springframework.context.annotation.Bean; 13 | 14 | /** 15 | * 初使化脱敏配置 16 | */ 17 | @AutoConfiguration 18 | @ConditionalOnProperty(value = "opensabre.sensitive.log.enabled", havingValue = "true") 19 | public class OpensabreSensitiveConfig { 20 | 21 | @Bean 22 | public BeanFactoryPostProcessor beanFactoryPostProcessor() { 23 | return configurableListableBeanFactory -> { 24 | LogBackCoreConverter logBackCoreConverter = LogBackCoreConverter.getInstance(); 25 | configurableListableBeanFactory.registerSingleton("logBackCoreConverter", logBackCoreConverter); 26 | configurableListableBeanFactory.registerSingleton("defaultDesensitizerStrategy", logBackCoreConverter.getDesensitizerStrategy()); 27 | }; 28 | } 29 | 30 | @Bean 31 | public LogBackDesensitizer regxLogBackDesensitizer() { 32 | return new RegxLogBackDesensitizer(); 33 | } 34 | 35 | @Bean 36 | public BeanPostProcessor opensabreSensitiveDesensitizerProcessor() { 37 | return new OpensabreSensitiveDesensitizerProcessor(); 38 | } 39 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/rest/MappingInfoHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.rest; 2 | 3 | import io.github.opensabre.boot.entity.RestMappingInfo; 4 | import org.springframework.web.bind.annotation.RequestMethod; 5 | import org.springframework.web.method.HandlerMethod; 6 | import org.springframework.web.servlet.mvc.method.RequestMappingInfo; 7 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; 8 | 9 | import jakarta.annotation.Resource; 10 | import java.util.HashSet; 11 | import java.util.Map; 12 | import java.util.Set; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * 获取springboot注册的Rest接口处理类 17 | * 18 | * @author zhoutaoo 19 | */ 20 | public class MappingInfoHandler { 21 | /** 22 | * RequestMappingHandlerMapping类,spring web的Rest注册管理类 23 | */ 24 | @Resource 25 | RequestMappingHandlerMapping requestMappingHandlerMapping; 26 | 27 | /** 28 | * 获取spring web应用所有注册的接口服务信息 29 | * 30 | * @return Set RestMappingInfo 31 | */ 32 | public Set getMappingInfo() { 33 | // 拿到Handler适配器中的全部方法 34 | Map methodMap = requestMappingHandlerMapping.getHandlerMethods(); 35 | Set interfaceInfos = new HashSet<>(); 36 | for (RequestMappingInfo requestMappingInfo : methodMap.keySet()) { 37 | Set urls = requestMappingInfo.getPathPatternsCondition().getPatternValues(); 38 | Set methods = requestMappingInfo.getMethodsCondition().getMethods(); 39 | Set interfaceInfoSet = urls.stream() 40 | .flatMap(url -> methods.stream().map(method -> new RestMappingInfo(url, method.name()))) 41 | .collect(Collectors.toSet()); 42 | interfaceInfos.addAll(interfaceInfoSet); 43 | } 44 | return interfaceInfos; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/core/util/UserContextHolder.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.util; 2 | 3 | import com.google.common.collect.Maps; 4 | 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | /** 9 | * 用户上下文 10 | */ 11 | public class UserContextHolder { 12 | /** 13 | * 用户名默认key 14 | */ 15 | public final String KEY_USERNAME = "user_name"; 16 | /** 17 | * 用于存储线程相关变量 18 | */ 19 | private final ThreadLocal> threadLocal; 20 | 21 | /** 22 | * 默认构造方法 23 | */ 24 | private UserContextHolder() { 25 | this.threadLocal = new ThreadLocal<>(); 26 | } 27 | 28 | /** 29 | * 创建实例 30 | * 31 | * @return UserContextHolder单例 32 | */ 33 | public static UserContextHolder getInstance() { 34 | return SingletonHolder.sInstance; 35 | } 36 | 37 | /** 38 | * 静态内部类单例模式 39 | * 单例初使化 40 | */ 41 | private static class SingletonHolder { 42 | /** 43 | * 使用静态变量返回单例 44 | */ 45 | private static final UserContextHolder sInstance = new UserContextHolder(); 46 | } 47 | 48 | /** 49 | * 用户上下文中放入信息 50 | * 51 | * @param map 上下文context参数 52 | */ 53 | public void setContext(Map map) { 54 | threadLocal.set(map); 55 | } 56 | 57 | /** 58 | * 获取上下文中的信息 59 | * 60 | * @return 返回上下文map 61 | */ 62 | public Map getContext() { 63 | return Optional.ofNullable(threadLocal.get()).orElse(Maps.newHashMap()); 64 | } 65 | 66 | /** 67 | * 获取上下文中的用户名 68 | * 69 | * @return 操作用户的用户名 70 | */ 71 | public String getUsername() { 72 | return Optional.ofNullable(threadLocal.get()).orElse(Maps.newHashMap()).get(KEY_USERNAME); 73 | } 74 | 75 | /** 76 | * 清空上下文 77 | */ 78 | public void clear() { 79 | threadLocal.remove(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/interceptor/UserInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.interceptor; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.github.opensabre.common.core.util.UserContextHolder; 5 | import jakarta.servlet.http.HttpServletRequest; 6 | import jakarta.servlet.http.HttpServletResponse; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.lang.Nullable; 9 | import org.springframework.web.servlet.HandlerInterceptor; 10 | 11 | 12 | import java.util.Map; 13 | 14 | /** 15 | * 用户信息拦截器 16 | */ 17 | public class UserInterceptor implements HandlerInterceptor { 18 | /** 19 | * 服务间调用token用户信息,格式为json 20 | * { 21 | * "user_name":"必须有" 22 | * "自定义key:"value" 23 | * } 24 | */ 25 | public static final String X_CLIENT_TOKEN_USER = "x-client-token-user"; 26 | /** 27 | * 服务间调用的认证token 28 | */ 29 | public static final String X_CLIENT_TOKEN = "x-client-token"; 30 | 31 | @Override 32 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 33 | //从网关获取并校验,通过校验就可信任x-client-token-user中的信息 34 | checkToken(request.getHeader(X_CLIENT_TOKEN)); 35 | String userInfoString = StringUtils.defaultIfBlank(request.getHeader(X_CLIENT_TOKEN_USER), "{}"); 36 | UserContextHolder.getInstance().setContext(new ObjectMapper().readValue(userInfoString, Map.class)); 37 | return true; 38 | } 39 | 40 | /** 41 | * 校验Token 42 | * 43 | * @param token 传来的token 44 | */ 45 | private void checkToken(String token) { 46 | //TODO 从网关获取并校验,通过校验就可信任x-client-token-user中的信息 47 | } 48 | 49 | @Override 50 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception { 51 | UserContextHolder.getInstance().clear(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /opensabre-starter-register/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-register 14 | jar 15 | 16 | opensabre-starter-register 17 | Opensabre Boot Register 18 | 19 | 20 | 21 | 22 | io.github.opensabre 23 | opensabre-base-dependencies 24 | ${revision} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | io.github.opensabre 34 | opensabre-starter-boot 35 | 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-bootstrap 40 | 41 | 42 | 43 | com.alibaba.cloud 44 | spring-cloud-starter-alibaba-nacos-discovery 45 | 46 | 47 | org.projectlombok 48 | lombok 49 | 50 | 51 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/LogBackCoreConverter.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log; 2 | 3 | import ch.qos.logback.classic.pattern.MessageConverter; 4 | import ch.qos.logback.classic.spi.ILoggingEvent; 5 | import io.github.opensabre.boot.sensitive.log.strategy.DefaultDesensitizerStrategy; 6 | import io.github.opensabre.boot.sensitive.log.strategy.DesensitizerStrategy; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | /** 10 | * 日志支持核心转换器 11 | * 12 | * @author zhoutaoo 13 | */ 14 | public class LogBackCoreConverter extends MessageConverter { 15 | /** 16 | * 实例引用 17 | */ 18 | private static LogBackCoreConverter logBackCoreConverter; 19 | 20 | /** 21 | * 默认日志脱敏策略 22 | */ 23 | private static DesensitizerStrategy desensitizerStrategy; 24 | 25 | /** 26 | * 默认构造器,初使化默认脱敏策略 27 | */ 28 | public LogBackCoreConverter() { 29 | logBackCoreConverter = this; 30 | if (desensitizerStrategy == null) 31 | desensitizerStrategy = new DefaultDesensitizerStrategy(); 32 | } 33 | 34 | /** 35 | * 获取实例 36 | * 37 | * @return 当前实例 38 | */ 39 | public static LogBackCoreConverter getInstance() { 40 | return logBackCoreConverter; 41 | } 42 | 43 | /** 44 | * 获取脱敏器 45 | * 46 | * @return 获取脱敏器 47 | */ 48 | public DesensitizerStrategy getDesensitizerStrategy() { 49 | return desensitizerStrategy; 50 | } 51 | 52 | /** 53 | * 设置脱敏器 54 | * 55 | * @param desensitizerStrategy 脱敏器 56 | */ 57 | public void setDesensitizerStrategy(DesensitizerStrategy desensitizerStrategy) { 58 | this.desensitizerStrategy = desensitizerStrategy; 59 | } 60 | 61 | @Override 62 | public String convert(ILoggingEvent event) { 63 | if (StringUtils.isBlank(event.getFormattedMessage())) 64 | return StringUtils.EMPTY; 65 | return desensitizerStrategy.desensitizing(event); 66 | } 67 | } -------------------------------------------------------------------------------- /opensabre-starter-cache/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-cache 14 | jar 15 | 16 | opensabre-starter-cache 17 | Opensabre Boot Cache 18 | 19 | 20 | 21 | 22 | io.github.opensabre 23 | opensabre-base-dependencies 24 | ${revision} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | com.alicp.jetcache 35 | jetcache-starter-redis-lettuce 36 | 37 | 38 | 39 | com.fasterxml.jackson.core 40 | jackson-annotations 41 | 42 | 43 | com.fasterxml.jackson.core 44 | jackson-databind 45 | 46 | 47 | 48 | org.projectlombok 49 | lombok 50 | 51 | 52 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/opensabre-boot.properties: -------------------------------------------------------------------------------- 1 | #==============springboot相关配置===============# 2 | spring.web.resources.add-mappings=true 3 | spring.mvc.format.date-time=yyyy-MM-dd HH:mm:ss 4 | spring.mvc.format.date=yyyy-MM-dd 5 | spring.servlet.multipart.max-request-size=2MB 6 | spring.servlet.multipart.max-file-size=5MB 7 | spring.jackson.time-zone=GMT+8 8 | spring.jackson.date-format=yyyy-MM-dd HH:mm:ss 9 | spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8000 10 | spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8000/oauth2/jwks 11 | #==============server相关配置===============# 12 | server.shutdown=graceful 13 | #==============敏感信息加密配置===============# 14 | jasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD:Pa55w0rd@opensabre} 15 | #==============管理端点===============# 16 | #启用配置里的info开头的变量 17 | management.info.env.enabled=true 18 | info.name=${spring.application.name} 19 | info.application.version=${git.build.version} 20 | info.java.version=${java.version} 21 | info.opensabre.version=${opensabre.version} 22 | info.opensabre.cloud.az=${opensabre.cloud.az} 23 | info.opensabre.cloud.region=${opensabre.cloud.region} 24 | #==============脱敏===============# 25 | opensabre.sensitive.log.enabled=false 26 | opensabre.sensitive.log.rules=mobile,idCard,phone 27 | #==============Rest报文===============# 28 | # 统一报文排除的包名,该包下的rest不使用统一报文,框架内置 29 | opensabre.rest.result.framework.excludes=org.springdoc 30 | # 统一报文排除的包名,该包下的rest不使用统一报文,应用级配置 31 | opensabre.rest.result.excludes= 32 | #==============Rest Swagger文档===============# 33 | opensabre.rest.swagger.version=v0.0.1 34 | opensabre.rest.swagger.title=Opensabre API 35 | opensabre.rest.swagger.description=Opensabre REST API 36 | opensabre.rest.swagger.licenseUrl=https://github.com/opensabre/opensabre-framework 37 | opensabre.rest.swagger.licenseName=Apache 2.0 38 | opensabre.rest.swagger.wikiUrl=https://opensabre.github.io/docs 39 | opensabre.rest.swagger.wikiDocumentation=Opensabre Wiki Documentation 40 | # knife4j的增强配置 41 | knife4j.enable=true 42 | knife4j.setting.language=zh_cn 43 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/desensitizer/PrefixLogBackDesensitizer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | import io.github.opensabre.boot.sensitive.rule.SensitiveRule; 5 | import lombok.extern.slf4j.Slf4j; 6 | 7 | import java.util.concurrent.atomic.AtomicReference; 8 | import java.util.regex.Matcher; 9 | 10 | /** 11 | * pattern的脱敏器实现 12 | * 姓名、密码类敏感信息在日志中没有特别明显的可识别特性,但一般在这些关键信息前都会有一些提示词, 13 | * 故此实现主要为匹配带有一定特别前缀的敏感信息: 14 | * name : 张三 15 | * password = 123456 16 | * 17 | * @author zhoutaoo 18 | */ 19 | @Slf4j 20 | public class PrefixLogBackDesensitizer extends AbstractLogBackDesensitizer { 21 | /** 22 | * 规则 23 | */ 24 | private final SensitiveRule sensitiveRule; 25 | /** 26 | * 匹配的关键词所在位置 27 | */ 28 | private final int keywordsGroupIndex; 29 | 30 | public PrefixLogBackDesensitizer(SensitiveRule sensitiveRule, int keywordsGroupIndex) { 31 | this.sensitiveRule = sensitiveRule; 32 | this.keywordsGroupIndex = keywordsGroupIndex; 33 | } 34 | 35 | /** 36 | * 判断日志内容中是否包含关键词等字样 37 | * 38 | * @param event 日志事件 39 | * @return true/false 40 | */ 41 | @Override 42 | public boolean support(ILoggingEvent event) { 43 | return sensitiveRule.pattern().matcher(event.getFormattedMessage()).find(); 44 | } 45 | 46 | /** 47 | * 将内容中匹配前缀关键词的内容替换为* 48 | * 49 | * @param event 日志事件 50 | * @param originStr 日志原始内容 51 | * @return 脱敏后内容 52 | */ 53 | @Override 54 | public String desensitizing(ILoggingEvent event, String originStr) { 55 | AtomicReference message = new AtomicReference<>(originStr); 56 | Matcher matcher = sensitiveRule.pattern().matcher(originStr); 57 | while (matcher.find()) { 58 | String passwd = matcher.group(keywordsGroupIndex); 59 | message.set(message.get().replaceAll(passwd, sensitiveRule.replace(passwd))); 60 | } 61 | return message.get(); 62 | } 63 | } -------------------------------------------------------------------------------- /opensabre-test/src/test/java/io/github/opensabre/common/test/PrivateHelperTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.test; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.reflect.Method; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | public class PrivateHelperTest { 11 | 12 | private final PrivateHelper instance = PrivateHelper.getInstance(); 13 | private PrivateObject privateObject; 14 | 15 | @BeforeEach 16 | public void before() { 17 | this.privateObject = new PrivateObject(); 18 | } 19 | 20 | @Test 21 | public void testSetPrivateField_假如对象有一个私有成员变量_当通过该方法给私有成员变量赋值_那么可以赋值成功() throws Exception { 22 | this.instance.setPrivateField(privateObject, "code", "123456"); 23 | assertEquals("123456", privateObject.getCode()); 24 | } 25 | 26 | @Test 27 | public void testInvokeMethod_假如对象有一个有参私有方法_当通过该方法调用私有方法_那么可以调用成功并返回结果() { 28 | Method method = this.instance.findMethod(privateObject, "changeCode", String.class); 29 | String code = (String) this.instance.invokePrivateMethod(privateObject, method, "abcef"); 30 | assertEquals("abcef", code); 31 | } 32 | 33 | @Test 34 | public void testInvokeMethod_假如对象有一个无参私有方法_当通过该方法调用私有方法_那么可以调用成功() { 35 | Method method = this.instance.findMethod(privateObject, "changeCode"); 36 | this.instance.invokePrivateMethod(privateObject, method); 37 | assertEquals("aaaaa", privateObject.getCode()); 38 | } 39 | 40 | @Test 41 | void testFindMethod_假如对象有一个无参私有方法_当通过该方法获取私有方法的信息_那么可以调用成功并返回结果() { 42 | Method method = this.instance.findMethod(privateObject, "changeCode"); 43 | assertEquals("changeCode", method.getName()); 44 | assertEquals(void.class, method.getReturnType()); 45 | } 46 | 47 | @Test 48 | void testFindMethodWithArgs_假如对象有一个有参私有方法_当通过该方法获取私有方法信息_那么可以调用成功并返回结果() { 49 | Method method = this.instance.findMethod(privateObject, "changeCode", String.class); 50 | assertEquals("changeCode", method.getName()); 51 | assertEquals(String.class, method.getReturnType()); 52 | } 53 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/java/io/github/opensabre/persistence/entity/po/BasePo.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.entity.po; 2 | 3 | import com.baomidou.mybatisplus.annotation.FieldFill; 4 | import com.baomidou.mybatisplus.annotation.IdType; 5 | import com.baomidou.mybatisplus.annotation.TableField; 6 | import com.baomidou.mybatisplus.annotation.TableId; 7 | import io.github.opensabre.common.web.entity.convert.EntityModelConverter; 8 | import io.github.opensabre.common.web.entity.vo.BaseVo; 9 | import io.swagger.v3.oas.annotations.media.Schema; 10 | import lombok.Data; 11 | import lombok.NoArgsConstructor; 12 | 13 | import java.io.Serializable; 14 | import java.util.Date; 15 | 16 | @Data 17 | @NoArgsConstructor 18 | public class BasePo implements Serializable { 19 | public final static String DEFAULT_USERNAME = "system"; 20 | 21 | @Schema(title = "主键", description = "存储数据记录的唯一ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100001") 22 | @TableId(type = IdType.ASSIGN_ID) 23 | private String id; 24 | 25 | @Schema(title = "创建人", description = "创建该数据记录人的唯一ID,默认为system", requiredMode = Schema.RequiredMode.REQUIRED, example = "admin") 26 | @TableField(fill = FieldFill.INSERT) 27 | private String createdBy; 28 | 29 | @Schema(title = "创建时间", description = "创建该数据记录的时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2021-04-09 21:00:00") 30 | @TableField(fill = FieldFill.INSERT) 31 | private Date createdTime; 32 | 33 | @Schema(title = "更新人", description = "更新该数据记录人的唯一ID,默认为system", requiredMode = Schema.RequiredMode.REQUIRED, example = "admin") 34 | @TableField(fill = FieldFill.INSERT_UPDATE) 35 | private String updatedBy; 36 | 37 | @Schema(title = "更新时间", description = "更新该数据记录的时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2021-04-10 21:00:00") 38 | @TableField(fill = FieldFill.INSERT_UPDATE) 39 | private Date updatedTime; 40 | 41 | /** 42 | * Po转化为Vo 43 | * 44 | * @param clazz Vo类 45 | * @return 返回Vo 46 | */ 47 | public V toVo(Class clazz) { 48 | return EntityModelConverter.getInstance().convert(this, clazz); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /opensabre-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | opensabre-framework 8 | io.github.opensabre 9 | ${revision} 10 | 11 | 12 | opensabre-test 13 | jar 14 | 15 | opensabre-test 16 | https://github.com/opensabre/ 17 | Opensabre Test project for Spring Boot 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 23 | 17 24 | 17 25 | 26 | 27 | 28 | 29 | 30 | opensabre-test 31 | https://github.com/opensabre/opensabre-framework/opensabre-test 32 | scm:git:https://github.com/opensabre/opensabre-framework.git 33 | scm:git:https://github.com/opensabre/opensabre-framework.git 34 | 35 | 36 | 37 | 38 | 39 | io.github.opensabre 40 | opensabre-base-dependencies 41 | ${revision} 42 | pom 43 | import 44 | 45 | 46 | 47 | 48 | 49 | 50 | org.springframework 51 | spring-core 52 | 53 | 54 | 55 | org.junit.jupiter 56 | junit-jupiter 57 | 58 | 59 | -------------------------------------------------------------------------------- /opensabre-starter-cache/src/main/resources/opensabre-cache.properties: -------------------------------------------------------------------------------- 1 | #==============缓存===============# 2 | jetcache.statIntervalMinutes=10 3 | jetcache.hidePackages=io.github.opensabre 4 | # 默认本地緩存5分钟 5 | jetcache.local.default.type=caffeine 6 | jetcache.local.default.keyConvertor=jackson 7 | jetcache.local.default.expireAfterWriteInMillis=300000 8 | jetcache.local.default.expireAfterAccessInMillis=180000 9 | # 長時本地緩存1小时,主要用于要求时效一般 10 | jetcache.local.longTime.type=caffeine 11 | jetcache.local.longTime.keyConvertor=jackson 12 | jetcache.local.longTime.expireAfterWriteInMillis=3600000 13 | jetcache.local.longTime.expireAfterAccessInMillis=1800000 14 | # 短時本地緩存1分钟,主要用于要求时效较高的配置 15 | jetcache.local.shortTime.type=caffeine 16 | jetcache.local.shortTime.keyConvertor=jackson 17 | jetcache.local.shortTime.expireAfterWriteInMillis=60000 18 | jetcache.local.shortTime.expireAfterAccessInMillis=40000 19 | 20 | # 默认2小时的远程缓存 21 | jetcache.remote.default.type=redis.lettuce 22 | jetcache.remote.default.expireAfterWriteInMillis=7200000 23 | jetcache.remote.default.keyConvertor=jackson 24 | jetcache.remote.default.valueEncoder=java 25 | jetcache.remote.default.valueDecoder=java 26 | jetcache.remote.default.poolConfig.minIdle=5 27 | jetcache.remote.default.poolConfig.maxIdle=20 28 | jetcache.remote.default.poolConfig.maxTotal=50 29 | jetcache.remote.default.uri=redis://${REDIS_HOST:localhost}:${REDIS_PORT:6379} 30 | # 长时远程緩存12小时,主要用于要求时效要求一般的集中式缓存 31 | jetcache.remote.longTime.type=redis.lettuce 32 | jetcache.remote.longTime.expireAfterWriteInMillis=43200000 33 | jetcache.remote.longTime.keyConvertor=jackson 34 | jetcache.remote.longTime.valueEncoder=java 35 | jetcache.remote.longTime.valueDecoder=java 36 | jetcache.remote.longTime.poolConfig.minIdle=5 37 | jetcache.remote.longTime.poolConfig.maxIdle=20 38 | jetcache.remote.longTime.poolConfig.maxTotal=50 39 | jetcache.remote.longTime.uri=redis://${REDIS_HOST:localhost}:${REDIS_PORT:6379} 40 | # 短時远程緩存5分钟,主要用于要求时效较高的集中式缓存 41 | jetcache.remote.shortTime.type=redis.lettuce 42 | jetcache.remote.shortTime.expireAfterWriteInMillis=300000 43 | jetcache.remote.shortTime.keyConvertor=jackson 44 | jetcache.remote.shortTime.valueEncoder=java 45 | jetcache.remote.shortTime.valueDecoder=java 46 | jetcache.remote.shortTime.poolConfig.minIdle=5 47 | jetcache.remote.shortTime.poolConfig.maxIdle=20 48 | jetcache.remote.shortTime.poolConfig.maxTotal=50 49 | jetcache.remote.shortTime.uri=redis://${REDIS_HOST:localhost}:${REDIS_PORT:6379} -------------------------------------------------------------------------------- /opensabre-test/src/main/java/io/github/opensabre/common/test/PrivateHelper.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.test; 2 | 3 | import org.springframework.util.ReflectionUtils; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | 8 | /** 9 | * 测试工具类,辅助测试使用 10 | * 11 | * @author zhoutaoo 12 | */ 13 | public class PrivateHelper { 14 | 15 | private PrivateHelper() { 16 | } 17 | 18 | /** 19 | * 创建实例 20 | * 21 | * @return PrivateHelper实例 22 | */ 23 | public static PrivateHelper getInstance() { 24 | return SingletPrivateHelper.sInstance; 25 | } 26 | 27 | /** 28 | * 静态内部类单例模式 29 | * 单例初使化 30 | */ 31 | private static class SingletPrivateHelper { 32 | private static final PrivateHelper sInstance = new PrivateHelper(); 33 | } 34 | 35 | /** 36 | * @param instance 实例对象 37 | * @param fieldName 成员变量名 38 | * @param value 值 39 | */ 40 | public void setPrivateField(Object instance, String fieldName, Object value) { 41 | Field signingKeyField = ReflectionUtils.findField(instance.getClass(), fieldName); 42 | ReflectionUtils.makeAccessible(signingKeyField); 43 | ReflectionUtils.setField(signingKeyField, instance, value); 44 | 45 | } 46 | 47 | /** 48 | * 寻找对象有参方法 49 | * 50 | * @param instance 实例对象 51 | * @param methodName 方法名 52 | * @param parameterTypes 方法参数类型 53 | * @return 对象方法 54 | */ 55 | public Method findMethod(Object instance, String methodName, Class... parameterTypes) { 56 | return ReflectionUtils.findMethod(instance.getClass(), methodName, parameterTypes); 57 | } 58 | 59 | /** 60 | * 寻找对象无参方法 61 | * 62 | * @param instance 实例对象 63 | * @param methodName 方法名 64 | * @return 对象方法 65 | */ 66 | public Method findMethod(Object instance, String methodName) { 67 | return ReflectionUtils.findMethod(instance.getClass(), methodName); 68 | } 69 | 70 | /** 71 | * 将么有方法设置为可访问,并调用该方法 72 | * 73 | * @param instance 实例对象 74 | * @param method 方法对象 75 | * @param args 实例的参数 76 | * @return 对象 77 | */ 78 | public Object invokePrivateMethod(Object instance, Method method, Object... args) { 79 | ReflectionUtils.makeAccessible(method); 80 | return ReflectionUtils.invokeMethod(method, instance, args); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /opensabre-starter-eda/src/main/java/io/github/opensabre/eda/config/OpensabreEdaConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.eda.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import io.github.opensabre.boot.config.YamlPropertyLoaderFactory; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.amqp.core.Binding; 9 | import org.springframework.amqp.core.BindingBuilder; 10 | import org.springframework.amqp.core.Queue; 11 | import org.springframework.amqp.core.TopicExchange; 12 | import org.springframework.amqp.support.converter.ContentTypeDelegatingMessageConverter; 13 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; 14 | import org.springframework.amqp.support.converter.MessageConverter; 15 | import org.springframework.boot.autoconfigure.AutoConfiguration; 16 | import org.springframework.context.annotation.Bean; 17 | import org.springframework.context.annotation.PropertySource; 18 | 19 | /** 20 | * 消息中心配置类 21 | * 22 | * @author zhoutaoo 23 | */ 24 | @Slf4j 25 | @AutoConfiguration 26 | @PropertySource(value = {"classpath:opensabre-eda.yml"}, encoding = "UTF8", factory = YamlPropertyLoaderFactory.class) 27 | public class OpensabreEdaConfig { 28 | 29 | public static final String QUEUE_NAME = "queue-organization"; 30 | public static final String EXCHANGE_NAME = "exchange-opensabre"; 31 | public static final String ROUTING_KEY = "routing-organization"; 32 | 33 | /** 34 | * test 35 | * 36 | * @return Queue 37 | */ 38 | @Bean 39 | Queue queue() { 40 | log.info("queue name:{}", QUEUE_NAME); 41 | return new Queue(QUEUE_NAME, false); 42 | } 43 | 44 | @Bean 45 | TopicExchange exchange() { 46 | log.info("exchange:{}", EXCHANGE_NAME); 47 | return new TopicExchange(EXCHANGE_NAME); 48 | } 49 | 50 | @Bean 51 | Binding binding(Queue queue, TopicExchange exchange) { 52 | log.info("binding {} to {} with {}", queue, exchange, ROUTING_KEY); 53 | return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY); 54 | } 55 | 56 | @Bean 57 | public MessageConverter messageConverter() { 58 | ObjectMapper objectMapper = new ObjectMapper(); 59 | objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 60 | return new ContentTypeDelegatingMessageConverter(new Jackson2JsonMessageConverter(objectMapper)); 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/log/desensitizer/RegxLogBackDesensitizer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.ILoggingEvent; 4 | import io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.beans.factory.annotation.Value; 8 | 9 | import jakarta.annotation.PostConstruct; 10 | import java.util.Arrays; 11 | import java.util.LinkedHashSet; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.concurrent.atomic.AtomicReference; 15 | import java.util.regex.Matcher; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * 正则脱敏器 20 | * 包含常用的敏感日志正则,如身份证、银行卡、手机号、姓名 21 | * 22 | * @author zhoutaoo 23 | */ 24 | @Slf4j 25 | public class RegxLogBackDesensitizer extends AbstractLogBackDesensitizer { 26 | 27 | @Value("${opensabre.sensitive.log.rules}") 28 | private String sensitiveRuleConfig; 29 | 30 | public Set sensitiveRules; 31 | 32 | @PostConstruct 33 | private void init() { 34 | Map sensitiveRuleMap = Arrays.stream(DefaultSensitiveRule.values()) 35 | .filter(rule -> StringUtils.contains(sensitiveRuleConfig, rule.category())) 36 | .collect(Collectors.toMap(DefaultSensitiveRule::category, rule -> rule)); 37 | // sensitiveRules Set按sensitiveRuleConfig顺序放置,越前优先级越高,越早匹配 38 | this.sensitiveRules = Arrays.stream(StringUtils.split(sensitiveRuleConfig, ",")) 39 | .map(sensitiveRuleMap::get) 40 | .collect(Collectors.toCollection(LinkedHashSet::new)); 41 | } 42 | 43 | @Override 44 | public boolean support(ILoggingEvent event) { 45 | // 任意匹配即需要脱敏 46 | return sensitiveRules.stream() 47 | .anyMatch(sensitiveRule -> sensitiveRule.pattern().matcher(event.getFormattedMessage()).find()); 48 | } 49 | 50 | @Override 51 | public String desensitizing(ILoggingEvent event, String originStr) { 52 | AtomicReference message = new AtomicReference<>(originStr); 53 | sensitiveRules.forEach(sensitiveRule -> { 54 | Matcher matcher = sensitiveRule.pattern().matcher(originStr); 55 | while (matcher.find()) { 56 | String matchStr = matcher.group(); 57 | message.set(message.get().replaceAll(matchStr, sensitiveRule.replace(matchStr))); 58 | } 59 | }); 60 | return message.get(); 61 | } 62 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/src/main/java/io/github/opensabre/persistence/config/MybatisConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.persistence.config; 2 | 3 | import com.baomidou.mybatisplus.annotation.DbType; 4 | import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 5 | import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; 6 | import com.baomidou.mybatisplus.extension.plugins.inner.IllegalSQLInnerInterceptor; 7 | import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; 8 | import io.github.opensabre.persistence.exception.PersistenceExceptionHandlerAdvice; 9 | import io.github.opensabre.persistence.handler.PoMetaObjectHandler; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.boot.autoconfigure.AutoConfiguration; 12 | import org.springframework.context.annotation.Bean; 13 | import org.springframework.context.annotation.Import; 14 | import org.springframework.context.annotation.PropertySource; 15 | import org.springframework.transaction.annotation.EnableTransactionManagement; 16 | 17 | 18 | @AutoConfiguration 19 | @EnableTransactionManagement 20 | @Import(PersistenceExceptionHandlerAdvice.class) 21 | @PropertySource(value = "classpath:opensabre-persistence.properties", encoding = "UTF8") 22 | public class MybatisConfig { 23 | 24 | @Value("${opensabre.persistence.interceptor.blockattack.enabled}") 25 | private Boolean blockattackEnabled; 26 | @Value("${opensabre.persistence.interceptor.illegalsql.enabled}") 27 | private Boolean illegalsqlEnabled; 28 | @Value("${opensabre.persistence.interceptor.pagination.enabled}") 29 | private Boolean paginationEnabled; 30 | @Value("${opensabre.persistence.interceptor.pagination.dbType}") 31 | private String paginationDbType; 32 | 33 | /** 34 | * 初使化Mybatis审计字段自动赋值 35 | */ 36 | @Bean 37 | public PoMetaObjectHandler poMetaObjectHandler() { 38 | return new PoMetaObjectHandler(); 39 | } 40 | 41 | /** 42 | * mybatis插件 43 | */ 44 | @Bean 45 | public MybatisPlusInterceptor mybatisPlusInterceptor() { 46 | MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); 47 | // 分页插件 48 | if (paginationEnabled) { 49 | interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.getDbType(paginationDbType))); 50 | } 51 | // 防止全表更新与删除 52 | if (blockattackEnabled) { 53 | interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); 54 | } 55 | // sql 性能规范插件 56 | if (illegalsqlEnabled) { 57 | interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor()); 58 | } 59 | return interceptor; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/sentinel/exception/SentinelExceptionHandlerAdvice.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.sentinel.exception; 2 | 3 | import com.alibaba.csp.sentinel.slots.block.BlockException; 4 | import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException; 5 | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; 6 | import com.alibaba.csp.sentinel.slots.block.flow.FlowException; 7 | import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException; 8 | import com.alibaba.csp.sentinel.slots.system.SystemBlockException; 9 | import io.github.opensabre.common.core.entity.vo.Result; 10 | import io.github.opensabre.common.core.exception.SystemErrorType; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.core.annotation.Order; 13 | import org.springframework.http.HttpStatus; 14 | import org.springframework.web.bind.annotation.ExceptionHandler; 15 | import org.springframework.web.bind.annotation.ResponseStatus; 16 | import org.springframework.web.bind.annotation.RestControllerAdvice; 17 | 18 | /** 19 | * 20 | */ 21 | @Slf4j 22 | @RestControllerAdvice 23 | @Order(100) 24 | public class SentinelExceptionHandlerAdvice { 25 | 26 | @ExceptionHandler(BlockException.class) 27 | @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS) 28 | public Result blockException(BlockException e) { 29 | log.error("block exception:{}", e.getRule()); 30 | return Result.fail(SystemErrorType.SYSTEM_BUSY); 31 | } 32 | 33 | @ExceptionHandler(FlowException.class) 34 | @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS) 35 | public Result flowException(FlowException e) { 36 | log.error("flow exception:{}", e.getRule()); 37 | return Result.fail(SystemErrorType.SYSTEM_BUSY); 38 | } 39 | 40 | @ExceptionHandler(DegradeException.class) 41 | @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS) 42 | public Result degradeException(DegradeException e) { 43 | log.error("degrade exception:{}", e.getRule()); 44 | return Result.fail(SystemErrorType.SYSTEM_BUSY); 45 | } 46 | 47 | @ExceptionHandler(ParamFlowException.class) 48 | public Result paramFlowException(ParamFlowException e) { 49 | log.error("param flow exception:{}", e.getRule()); 50 | return Result.fail(SystemErrorType.SYSTEM_BUSY); 51 | } 52 | 53 | @ExceptionHandler(SystemBlockException.class) 54 | public Result systemBlockException(SystemBlockException e) { 55 | log.error("system block exception:{}", e.getRule()); 56 | return Result.fail(SystemErrorType.SYSTEM_BUSY); 57 | } 58 | 59 | @ExceptionHandler(AuthorityException.class) 60 | public Result authorityException(AuthorityException e) { 61 | log.error("authority exception:{}", e.getRule()); 62 | return Result.fail(SystemErrorType.SYSTEM_BUSY); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/OpensabreSwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | import io.github.opensabre.boot.entity.SwaggerInfo; 4 | import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; 5 | import io.swagger.v3.oas.annotations.security.*; 6 | import io.swagger.v3.oas.models.ExternalDocumentation; 7 | import io.swagger.v3.oas.models.OpenAPI; 8 | import io.swagger.v3.oas.models.info.Info; 9 | import io.swagger.v3.oas.models.info.License; 10 | import org.springframework.boot.autoconfigure.AutoConfiguration; 11 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 12 | import org.springframework.context.annotation.Bean; 13 | 14 | import jakarta.annotation.Resource; 15 | 16 | /** 17 | * Swagger配置类 18 | */ 19 | @AutoConfiguration 20 | @EnableConfigurationProperties(SwaggerInfo.class) 21 | @SecuritySchemes({@SecurityScheme( 22 | // 指定 SecurityScheme 的名称(OpenAPIDefinition注解中的security属性中会引用该名称) 23 | name = "${custom.security.name}", 24 | // 指定认证类型为oauth2 25 | type = SecuritySchemeType.OAUTH2, 26 | // 设置认证流程 27 | flows = @OAuthFlows( 28 | // 设置授权码模式 29 | authorizationCode = @OAuthFlow( 30 | // 获取token地址 31 | tokenUrl = "${custom.security.token-url}", 32 | // 授权申请地址 33 | authorizationUrl = "${custom.security.authorization-url}", 34 | // oauth2的申请的scope(需要在OAuth2客户端中存在) 35 | scopes = { 36 | @OAuthScope(name = "openid", description = "OpenId登录"), 37 | @OAuthScope(name = "profile", description = "获取用户信息"), 38 | @OAuthScope(name = "message.read", description = "读"), 39 | @OAuthScope(name = "message.write", description = "写") 40 | }) 41 | ) 42 | )}) 43 | public class OpensabreSwaggerConfig { 44 | 45 | @Resource 46 | private SwaggerInfo swaggerInfo; 47 | 48 | /** 49 | * Swagger配置对象初使化 50 | * 51 | * @return OpenAPI 52 | */ 53 | @Bean 54 | public OpenAPI openAPI() { 55 | return new OpenAPI() 56 | .info(new Info() 57 | .title(swaggerInfo.getTitle()) 58 | .description(swaggerInfo.getDescription()) 59 | .version(swaggerInfo.getVersion()) 60 | .license(new License().name(swaggerInfo.getLicenseName()).url(swaggerInfo.getLicenseUrl()))) 61 | // 外部文档 62 | .externalDocs(new ExternalDocumentation() 63 | .description(swaggerInfo.getWikiDocumentation()) 64 | .url(swaggerInfo.getWikiUrl())); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rule/CustomSensitiveRule.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rule; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | /** 6 | * 自定义脱敏规则 7 | */ 8 | public class CustomSensitiveRule implements SensitiveRule { 9 | /** 10 | * 正则表达式 11 | */ 12 | private final Pattern pattern; 13 | /** 14 | * 保留前缀个数 15 | */ 16 | private final int retainPrefixCount; 17 | /** 18 | * 保留后缀个数 19 | */ 20 | private final int retainSuffixCount; 21 | /** 22 | * 掩码符号,默认为* 23 | */ 24 | private char replaceChar = '*'; 25 | 26 | /** 27 | * 定义保留后/后缀个数的构造方法 28 | * 29 | * @param retainPrefixCount 保留前缀个数 30 | * @param retainSuffixCount 保留后缀个数 31 | */ 32 | public CustomSensitiveRule(int retainPrefixCount, int retainSuffixCount) { 33 | this.pattern = Pattern.compile("\\*"); 34 | this.retainPrefixCount = retainPrefixCount; 35 | this.retainSuffixCount = retainSuffixCount; 36 | } 37 | 38 | /** 39 | * @param retainPrefixCount 保留前缀个数 40 | * @param retainSuffixCount 保留后缀个数 41 | * @param replaceChar 掩码符号 42 | */ 43 | public CustomSensitiveRule(int retainPrefixCount, int retainSuffixCount, char replaceChar) { 44 | this.pattern = Pattern.compile("\\*"); 45 | this.retainPrefixCount = retainPrefixCount; 46 | this.retainSuffixCount = retainSuffixCount; 47 | this.replaceChar = replaceChar; 48 | } 49 | 50 | /** 51 | * @param pattern 正则表达式 52 | * @param retainPrefixCount 保留前缀个数 53 | * @param retainSuffixCount 保留后缀个数 54 | */ 55 | public CustomSensitiveRule(String pattern, int retainPrefixCount, int retainSuffixCount) { 56 | this.pattern = Pattern.compile(pattern); 57 | this.retainPrefixCount = retainPrefixCount; 58 | this.retainSuffixCount = retainSuffixCount; 59 | } 60 | 61 | /** 62 | * @param pattern 正则表达式 63 | * @param retainPrefixCount 保留前缀个数 64 | * @param retainSuffixCount 保留后缀个数 65 | * @param replaceChar 掩码符号 66 | */ 67 | public CustomSensitiveRule(String pattern, int retainPrefixCount, int retainSuffixCount, char replaceChar) { 68 | this.pattern = Pattern.compile(pattern); 69 | this.retainPrefixCount = retainPrefixCount; 70 | this.retainSuffixCount = retainSuffixCount; 71 | this.replaceChar = replaceChar; 72 | } 73 | 74 | @Override 75 | public String category() { 76 | return "custom"; 77 | } 78 | 79 | @Override 80 | public Pattern pattern() { 81 | return this.pattern; 82 | } 83 | 84 | @Override 85 | public int retainPrefixCount() { 86 | return this.retainPrefixCount; 87 | } 88 | 89 | @Override 90 | public int retainSuffixCount() { 91 | return this.retainSuffixCount; 92 | } 93 | 94 | @Override 95 | public char replaceChar() { 96 | return this.replaceChar; 97 | } 98 | } -------------------------------------------------------------------------------- /opensabre-starter-persistence/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-persistence 14 | jar 15 | 16 | opensabre-starter-persistence 17 | Opensabre Boot Persistence 18 | 19 | 20 | 1.5.5.Final 21 | 22 | 23 | 24 | 25 | 26 | io.github.opensabre 27 | opensabre-base-dependencies 28 | ${revision} 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | 37 | io.github.opensabre 38 | opensabre-starter-boot 39 | 40 | 41 | 42 | org.projectlombok 43 | lombok 44 | 45 | 46 | org.apache.commons 47 | commons-lang3 48 | 49 | 50 | io.github.opensabre 51 | opensabre-web 52 | 53 | 54 | 55 | com.baomidou 56 | mybatis-plus-boot-starter 57 | 58 | 59 | com.baomidou 60 | mybatis-plus-extension 61 | 62 | 63 | org.mybatis 64 | mybatis-spring 65 | 66 | 67 | 68 | com.mysql 69 | mysql-connector-j 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-starter-test 75 | test 76 | 77 | 78 | 79 | org.junit.jupiter 80 | junit-jupiter 81 | test 82 | 83 | 84 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/config/OpensabreEnvConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.config; 2 | 3 | import cn.hutool.core.io.resource.NoResourceException; 4 | import cn.hutool.setting.dialect.Props; 5 | import io.github.opensabre.boot.metadata.OpensabreCloud; 6 | import io.github.opensabre.boot.metadata.OpensabreVersion; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.env.EnvironmentPostProcessor; 10 | import org.springframework.core.env.ConfigurableEnvironment; 11 | import org.springframework.core.env.MapPropertySource; 12 | import org.springframework.core.env.MutablePropertySources; 13 | import org.springframework.core.env.PropertiesPropertySource; 14 | 15 | import java.util.HashMap; 16 | 17 | import static io.github.opensabre.boot.metadata.OpensabreCloud.OPENSABRE_CLOUD_AZ; 18 | import static io.github.opensabre.boot.metadata.OpensabreCloud.OPENSABRE_CLOUD_REGION; 19 | import static io.github.opensabre.boot.metadata.OpensabreVersion.OPENSABRE_FORMATTED_VERSION; 20 | import static io.github.opensabre.boot.metadata.OpensabreVersion.OPENSABRE_VERSION; 21 | 22 | /** 23 | * Opensabre环境变更初使化 24 | * 将版本号初使化到环境变更中,方便应用后续扩展使用 25 | */ 26 | @Slf4j 27 | public class OpensabreEnvConfig implements EnvironmentPostProcessor { 28 | 29 | @Override 30 | public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { 31 | log.info("OpensabreEnvConfig init start"); 32 | MutablePropertySources propertySources = environment.getPropertySources(); 33 | propertySources.addFirst(getVersionEnv()); 34 | propertySources.addFirst(getCloudEnv()); 35 | propertySources.addFirst(getGitProperties()); 36 | } 37 | 38 | /** 39 | * 组装好opensabre框架元数据 40 | * 41 | * @return MapPropertySource 42 | */ 43 | private MapPropertySource getVersionEnv() { 44 | HashMap versionMap = new HashMap<>(); 45 | versionMap.put(OPENSABRE_VERSION, OpensabreVersion.getVersion()); 46 | versionMap.put(OPENSABRE_FORMATTED_VERSION, OpensabreVersion.getVersionString()); 47 | return new MapPropertySource("version", versionMap); 48 | } 49 | 50 | /** 51 | * 组装好opensabre云环境元数据 52 | * 53 | * @return MapPropertySource 54 | */ 55 | private MapPropertySource getCloudEnv() { 56 | HashMap propertyMap = new HashMap<>(); 57 | propertyMap.put(OPENSABRE_CLOUD_AZ, OpensabreCloud.getCloudAz()); 58 | propertyMap.put(OPENSABRE_CLOUD_REGION, OpensabreCloud.getCloudRegion()); 59 | return new MapPropertySource("cloud", propertyMap); 60 | } 61 | 62 | /** 63 | * 加载git.properties构建元数据 64 | * 65 | * @return PropertiesPropertySource 66 | */ 67 | private PropertiesPropertySource getGitProperties() { 68 | Props props = new Props(); 69 | try { 70 | props = new Props("git.properties"); 71 | } catch (NoResourceException exception) { 72 | log.warn("load git properties: {}", exception.getMessage()); 73 | } 74 | return new PropertiesPropertySource("application", props.toProperties()); 75 | } 76 | } -------------------------------------------------------------------------------- /opensabre-starter-rpc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-rpc 14 | jar 15 | 16 | opensabre-starter-rpc 17 | Opensabre Boot Rpc 18 | 19 | 20 | 21 | 22 | io.github.opensabre 23 | opensabre-base-dependencies 24 | ${revision} 25 | pom 26 | import 27 | 28 | 29 | 30 | 31 | 32 | 33 | io.github.opensabre 34 | opensabre-starter-register 35 | 36 | 37 | 38 | org.springframework.cloud 39 | spring-cloud-starter-openfeign 40 | 41 | 42 | 43 | org.springframework.cloud 44 | spring-cloud-starter-loadbalancer 45 | 46 | 47 | 48 | io.github.openfeign 49 | feign-hc5 50 | 51 | 52 | 53 | io.github.openfeign 54 | feign-micrometer 55 | 56 | 57 | 58 | com.alibaba.cloud 59 | spring-cloud-starter-alibaba-sentinel 60 | 61 | 62 | 63 | com.alibaba.cloud 64 | spring-cloud-alibaba-sentinel-datasource 65 | 66 | 67 | 68 | com.alibaba.csp 69 | sentinel-datasource-nacos 70 | 71 | 72 | 73 | com.google.guava 74 | guava 75 | 76 | 77 | 78 | jakarta.servlet 79 | jakarta.servlet-api 80 | provided 81 | 82 | 83 | org.projectlombok 84 | lombok 85 | 86 | 87 | -------------------------------------------------------------------------------- /opensabre-starter-rpc/src/main/java/io/github/opensabre/rpc/loadbalance/OpensabreLoadBalancer.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.rpc.loadbalance; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.ObjectProvider; 5 | import org.springframework.cloud.client.ServiceInstance; 6 | import org.springframework.cloud.client.loadbalancer.*; 7 | import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; 8 | import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; 9 | import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback; 10 | import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.List; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | 16 | 17 | /** 18 | * Opensabre负载均衡器 19 | */ 20 | @Slf4j 21 | public class OpensabreLoadBalancer implements ReactorServiceInstanceLoadBalancer { 22 | 23 | final AtomicInteger position; 24 | 25 | final String serviceId; 26 | 27 | ObjectProvider serviceInstanceListSupplierProvider; 28 | 29 | /** 30 | * @param serviceInstanceListSupplierProvider 服务节点 31 | * @param serviceId 服务ID,应用名 32 | */ 33 | public OpensabreLoadBalancer(ObjectProvider serviceInstanceListSupplierProvider, String serviceId) { 34 | this.serviceId = serviceId; 35 | this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; 36 | this.position = new AtomicInteger(90); 37 | } 38 | 39 | @Override 40 | public Mono> choose(Request request) { 41 | RequestDataContext context = (RequestDataContext) request.getContext(); 42 | RequestData clientRequest = context.getClientRequest(); 43 | 44 | ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new); 45 | return supplier.get(request).next().map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); 46 | } 47 | 48 | /** 49 | * @param supplier 服务实例提供者 50 | * @param serviceInstances 可用服务实例 51 | * @return Response ServiceInstance 52 | */ 53 | private Response processInstanceResponse(ServiceInstanceListSupplier supplier, 54 | List serviceInstances) { 55 | Response serviceInstanceResponse = getInstanceResponse(serviceInstances); 56 | if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { 57 | ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); 58 | } 59 | return serviceInstanceResponse; 60 | } 61 | 62 | /** 63 | * @param instances 可用实例 64 | * @return Response ServiceInstance 65 | */ 66 | private Response getInstanceResponse(List instances) { 67 | if (instances.isEmpty()) { 68 | log.warn("No servers available for service: " + serviceId); 69 | return new EmptyResponse(); 70 | } 71 | //此处编写自己的负载均衡策略 72 | int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; 73 | ServiceInstance instance = instances.get(pos % instances.size()); 74 | return new DefaultResponse(instance); 75 | } 76 | } -------------------------------------------------------------------------------- /opensabre-web/src/test/java/io/github/opensabre/common/web/exception/DefaultGlobalExceptionHandlerAdviceTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.exception; 2 | 3 | import io.github.opensabre.common.core.entity.vo.Result; 4 | import io.github.opensabre.common.core.exception.BaseException; 5 | import io.github.opensabre.common.core.exception.SystemErrorType; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | import org.springframework.core.MethodParameter; 9 | import org.springframework.validation.BindException; 10 | import org.springframework.validation.BindingResult; 11 | import org.springframework.validation.FieldError; 12 | import org.springframework.web.bind.MethodArgumentNotValidException; 13 | import org.springframework.web.bind.MissingServletRequestParameterException; 14 | import org.springframework.web.multipart.MultipartException; 15 | 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | import static org.mockito.Mockito.mock; 18 | import static org.mockito.Mockito.when; 19 | 20 | public class DefaultGlobalExceptionHandlerAdviceTest { 21 | 22 | private DefaultGlobalExceptionHandlerAdvice defaultGlobalExceptionHandlerAdvice; 23 | 24 | @BeforeEach 25 | public void before() { 26 | this.defaultGlobalExceptionHandlerAdvice = new DefaultGlobalExceptionHandlerAdvice(); 27 | } 28 | 29 | @Test 30 | public void testMissingServletRequestParameterException() { 31 | Result result = defaultGlobalExceptionHandlerAdvice 32 | .missingServletRequestParameterException(new MissingServletRequestParameterException("test", "string")); 33 | assertEquals(SystemErrorType.ARGUMENT_NOT_VALID.getCode(), result.getCode()); 34 | } 35 | 36 | @Test 37 | public void testUploadFileLimitException() { 38 | Result result = defaultGlobalExceptionHandlerAdvice.uploadFileLimitException(new MultipartException("test")); 39 | assertEquals(SystemErrorType.UPLOAD_FILE_SIZE_LIMIT.getCode(), result.getCode()); 40 | } 41 | 42 | @Test 43 | public void testServiceException() throws NoSuchMethodException { 44 | //准备数据 45 | MethodParameter methodParameter = new MethodParameter(Object.class.getMethod("toString"), -1); 46 | BindingResult bindingResult = mock(BindException.class); 47 | FieldError fieldError = new FieldError("test", "test", "testmessage"); 48 | when(bindingResult.getFieldError()).thenReturn(fieldError); 49 | MethodArgumentNotValidException exception = new MethodArgumentNotValidException(methodParameter, bindingResult); 50 | //调用 51 | Result result = defaultGlobalExceptionHandlerAdvice.argumentInvalidException(exception); 52 | //验证 53 | assertEquals(SystemErrorType.ARGUMENT_NOT_VALID.getCode(), result.getCode()); 54 | assertEquals("testmessage", result.getData()); 55 | } 56 | 57 | @Test 58 | public void testBaseException() { 59 | Result result = defaultGlobalExceptionHandlerAdvice.baseException(new BaseException(SystemErrorType.SYSTEM_BUSY)); 60 | assertEquals(SystemErrorType.SYSTEM_BUSY.getCode(), result.getCode()); 61 | } 62 | 63 | @Test 64 | public void testException() { 65 | Result result = defaultGlobalExceptionHandlerAdvice.exception(new Exception("error")); 66 | assertEquals(SystemErrorType.SYSTEM_ERROR.getCode(), result.getCode()); 67 | } 68 | 69 | @Test 70 | public void testThrowable() { 71 | Result result = defaultGlobalExceptionHandlerAdvice.exception(new Throwable("error")); 72 | assertEquals(SystemErrorType.SYSTEM_ERROR.getCode(), result.getCode()); 73 | } 74 | } -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/rest/RestResponseBodyAdvice.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.rest; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import io.github.opensabre.common.core.entity.vo.Result; 5 | import jakarta.validation.constraints.NotNull; 6 | import lombok.SneakyThrows; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.core.MethodParameter; 10 | import org.springframework.core.annotation.AnnotatedElementUtils; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.http.converter.HttpMessageConverter; 13 | import org.springframework.http.server.ServerHttpRequest; 14 | import org.springframework.http.server.ServerHttpResponse; 15 | import org.springframework.web.bind.annotation.ResponseBody; 16 | import org.springframework.web.bind.annotation.RestController; 17 | import org.springframework.web.bind.annotation.RestControllerAdvice; 18 | import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 19 | 20 | import java.util.Objects; 21 | 22 | /** 23 | * Rest统一返回报文封装,在rest方法返回后送给客户端前执行 24 | */ 25 | @RestControllerAdvice 26 | public class RestResponseBodyAdvice implements ResponseBodyAdvice { 27 | /** 28 | * 框架中不需要包装为Result对象的包名 29 | */ 30 | @Value("${opensabre.rest.result.framework.excludes}") 31 | private String excludeFrameworkPackageStr; 32 | /** 33 | * 应用中不需要包装为Result对象的包名 34 | */ 35 | @Value("${opensabre.rest.result.excludes}") 36 | private String excludePackageStr; 37 | 38 | @Override 39 | public boolean supports(@NotNull MethodParameter returnType, 40 | @NotNull Class> converterType) { 41 | // 如果不需要进行封装的,可以添加一些校验手段,比如添加标记排除的注解 42 | Class returnTypeContainingClass = returnType.getContainingClass(); 43 | boolean hasResponseBody = AnnotatedElementUtils.hasAnnotation(returnTypeContainingClass, ResponseBody.class) || 44 | returnType.hasMethodAnnotation(ResponseBody.class); 45 | boolean hasRestController = AnnotatedElementUtils.hasAnnotation(returnTypeContainingClass, RestController.class) || 46 | returnType.hasMethodAnnotation(RestController.class); 47 | return hasResponseBody && hasRestController && isNeedWrap(returnTypeContainingClass.getPackageName()); 48 | } 49 | 50 | /** 51 | * 是否需要包装为Result统一报文 52 | * 53 | * @param packageName 包名 54 | * @return 是否需要 true/false 55 | */ 56 | private boolean isNeedWrap(String packageName) { 57 | String delimiter = ","; 58 | String allExcludePackageStr = StringUtils.joinWith(delimiter, excludeFrameworkPackageStr, excludePackageStr); 59 | return !StringUtils.startsWithAny(packageName, StringUtils.split(allExcludePackageStr, delimiter)); 60 | } 61 | 62 | @SneakyThrows 63 | @Override 64 | public Object beforeBodyWrite(Object body, 65 | @NotNull MethodParameter returnType, 66 | @NotNull MediaType selectedContentType, 67 | @NotNull Class> selectedConverterType, 68 | @NotNull ServerHttpRequest request, 69 | @NotNull ServerHttpResponse response) { 70 | // 提供一定的灵活度,如果body已经被包装了,就不进行包装 71 | if (body instanceof Result) { 72 | return body; 73 | } 74 | // 如果是String,将Result转为json string 75 | if (Objects.isNull(body) || body instanceof String) { 76 | return new ObjectMapper().writeValueAsString(Result.success(body)); 77 | } 78 | return Result.success(body); 79 | } 80 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rest/DesensitizationSerialize.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rest; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.BeanProperty; 5 | import com.fasterxml.jackson.databind.JsonMappingException; 6 | import com.fasterxml.jackson.databind.JsonSerializer; 7 | import com.fasterxml.jackson.databind.SerializerProvider; 8 | import com.fasterxml.jackson.databind.ser.ContextualSerializer; 9 | import io.github.opensabre.boot.annotations.Desensitization; 10 | import io.github.opensabre.boot.sensitive.rest.strategy.CustomSensitiveStrategy; 11 | import io.github.opensabre.boot.sensitive.rest.strategy.DefaultSensitiveStrategy; 12 | import io.github.opensabre.boot.sensitive.rest.strategy.SensitiveStrategy; 13 | import io.github.opensabre.boot.sensitive.rule.CustomSensitiveRule; 14 | import io.github.opensabre.boot.sensitive.rule.DefaultSensitiveRule; 15 | import io.github.opensabre.boot.sensitive.rule.SensitiveRule; 16 | import lombok.NoArgsConstructor; 17 | 18 | import java.io.IOException; 19 | import java.util.Objects; 20 | import java.util.Optional; 21 | 22 | /** 23 | * 脱敏数据处理类 24 | */ 25 | @NoArgsConstructor 26 | public class DesensitizationSerialize extends JsonSerializer implements ContextualSerializer { 27 | /** 28 | * 脱敏策略 29 | */ 30 | private SensitiveStrategy sensitiveStrategy; 31 | /** 32 | * 脱敏规则 33 | */ 34 | private SensitiveRule type; 35 | 36 | /** 37 | * 默认策略 38 | * 39 | * @param type 脱敏类型 40 | */ 41 | public DesensitizationSerialize(SensitiveRule type) { 42 | this.type = type; 43 | this.sensitiveStrategy = new DefaultSensitiveStrategy(); 44 | } 45 | 46 | /** 47 | * 自定义脱敏类型 48 | * 49 | * @param retainPrefixCount 保留前缀个数 50 | * @param retainSuffixCount 保留后缀个数 51 | * @param replaceChar 掩码字符 52 | */ 53 | public DesensitizationSerialize(int retainPrefixCount, int retainSuffixCount, char replaceChar) { 54 | CustomSensitiveRule sensitiveRule = new CustomSensitiveRule(retainPrefixCount, retainSuffixCount, replaceChar); 55 | this.type = sensitiveRule; 56 | this.sensitiveStrategy = new CustomSensitiveStrategy(sensitiveRule); 57 | } 58 | 59 | @Override 60 | public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 61 | jsonGenerator.writeString(sensitiveStrategy.desensitizing(type, str)); 62 | } 63 | 64 | @Override 65 | public JsonSerializer createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException { 66 | if (Objects.isNull(beanProperty)) { 67 | return serializerProvider.findNullValueSerializer(null); 68 | } 69 | // 判断数据类型是否为String类型 70 | if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) { 71 | // 获取定义的注解 72 | Desensitization desensitization = Optional.ofNullable(beanProperty.getAnnotation(Desensitization.class)) 73 | .orElse(beanProperty.getContextAnnotation(Desensitization.class)); 74 | // 不为null 75 | if (desensitization != null) { 76 | // 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。 77 | if (desensitization.type().equals(DefaultSensitiveRule.CUSTOM)) 78 | return new DesensitizationSerialize(desensitization.retainPrefixCount(), 79 | desensitization.retainSuffixCount(), 80 | desensitization.replaceChar()); 81 | else 82 | return new DesensitizationSerialize(desensitization.type()); 83 | } 84 | } 85 | return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty); 86 | } 87 | } -------------------------------------------------------------------------------- /opensabre-web/src/test/java/io/github/opensabre/common/core/entity/vo/ResultTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.entity.vo; 2 | 3 | import io.github.opensabre.common.core.exception.BaseException; 4 | import io.github.opensabre.common.core.exception.SystemErrorType; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import java.util.Date; 8 | 9 | import static org.junit.jupiter.api.Assertions.*; 10 | 11 | class ResultTest { 12 | @Test 13 | void testConstruct() { 14 | Result result = new Result(); 15 | assertTrue(result.isSuccess()); 16 | assertEquals(result.getCode(), "000000"); 17 | assertEquals(result.getMesg(), "处理成功"); 18 | } 19 | 20 | @Test 21 | void testConstructErrorType() { 22 | Result result = new Result(SystemErrorType.SYSTEM_BUSY); 23 | assertTrue(result.isFail()); 24 | assertEquals(result.getCode(), SystemErrorType.SYSTEM_BUSY.getCode()); 25 | assertEquals(result.getMesg(), SystemErrorType.SYSTEM_BUSY.getMesg()); 26 | } 27 | 28 | @Test 29 | void testConstructErrorTypeWithData() { 30 | Date data = new Date(); 31 | Result result = new Result(SystemErrorType.SYSTEM_BUSY, data); 32 | assertTrue(result.isFail()); 33 | assertEquals(result.getCode(), SystemErrorType.SYSTEM_BUSY.getCode()); 34 | assertEquals(result.getMesg(), SystemErrorType.SYSTEM_BUSY.getMesg()); 35 | assertEquals(result.getData(), data); 36 | } 37 | 38 | @Test 39 | void testSuccess() { 40 | Result success = Result.success(); 41 | assertTrue(success.isSuccess()); 42 | assertEquals(success.getCode(), "000000"); 43 | assertEquals(success.getMesg(), "处理成功"); 44 | } 45 | 46 | @Test 47 | void testSuccessWithData() { 48 | Date data = new Date(); 49 | Result result = Result.success(data); 50 | assertTrue(result.isSuccess()); 51 | assertEquals(result.getData(), data); 52 | } 53 | 54 | @Test 55 | void testFail() { 56 | Result result = Result.fail(); 57 | assertFalse(result.isSuccess()); 58 | assertEquals(result.getCode(), "-1"); 59 | assertEquals(result.getMesg(), "系统异常"); 60 | } 61 | 62 | @Test 63 | void failWithData() { 64 | Date data = new Date(); 65 | Result result = Result.fail(data); 66 | assertFalse(result.isSuccess()); 67 | assertEquals(result.getData(), data); 68 | } 69 | 70 | @Test 71 | void testFailWithBaseException() { 72 | Result result = Result.fail(new BaseException()); 73 | assertTrue(result.isFail()); 74 | assertEquals(result.getCode(), SystemErrorType.SYSTEM_ERROR.getCode()); 75 | } 76 | 77 | @Test 78 | void testFailWithBaseExceptionAndData() { 79 | Date data = new Date(); 80 | Result result = Result.fail(new BaseException(), data); 81 | assertTrue(result.isFail()); 82 | assertEquals(result.getCode(), SystemErrorType.SYSTEM_ERROR.getCode()); 83 | assertEquals(result.getData(), data); 84 | } 85 | 86 | @Test 87 | void testFailWithErrorTypeAndData() { 88 | Date data = new Date(); 89 | Result result = Result.fail(SystemErrorType.GATEWAY_ERROR, data); 90 | assertTrue(result.isFail()); 91 | assertEquals(result.getCode(), SystemErrorType.GATEWAY_ERROR.getCode()); 92 | assertEquals(result.getData(), data); 93 | } 94 | 95 | @Test 96 | void testFailWithErrorType() { 97 | Result result = Result.fail(SystemErrorType.GATEWAY_ERROR); 98 | assertTrue(result.isFail()); 99 | assertEquals(result.getCode(), SystemErrorType.GATEWAY_ERROR.getCode()); 100 | } 101 | 102 | @Test 103 | void isSuccess() { 104 | assertTrue(Result.success().isSuccess()); 105 | } 106 | 107 | @Test 108 | void isFail() { 109 | assertTrue(Result.fail().isFail()); 110 | } 111 | } -------------------------------------------------------------------------------- /opensabre-starter-webmvc/src/main/java/io/github/opensabre/webmvc/exception/DefaultWebMvcExceptionHandlerAdvice.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.webmvc.exception; 2 | 3 | import io.github.opensabre.common.core.entity.vo.Result; 4 | import io.github.opensabre.common.core.exception.SystemErrorType; 5 | import jakarta.servlet.ServletException; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.core.annotation.Order; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.converter.HttpMessageNotReadableException; 10 | import org.springframework.web.HttpMediaTypeNotSupportedException; 11 | import org.springframework.web.HttpRequestMethodNotSupportedException; 12 | import org.springframework.web.bind.MethodArgumentNotValidException; 13 | import org.springframework.web.bind.MissingServletRequestParameterException; 14 | import org.springframework.web.bind.annotation.ExceptionHandler; 15 | import org.springframework.web.bind.annotation.ResponseStatus; 16 | import org.springframework.web.bind.annotation.RestControllerAdvice; 17 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; 18 | import org.springframework.web.multipart.MultipartException; 19 | import org.springframework.web.servlet.NoHandlerFoundException; 20 | import org.springframework.web.servlet.resource.NoResourceFoundException; 21 | 22 | /** 23 | * 默认全局异常处理类 24 | */ 25 | @Slf4j 26 | @Order 27 | @RestControllerAdvice 28 | public class DefaultWebMvcExceptionHandlerAdvice { 29 | 30 | @ExceptionHandler(value = {MissingServletRequestParameterException.class}) 31 | public Result missingServletRequestParameterException(MissingServletRequestParameterException ex) { 32 | log.warn("missing servlet request parameter exception:{}", ex.getMessage()); 33 | return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID); 34 | } 35 | 36 | @ExceptionHandler(value = {MethodArgumentNotValidException.class}) 37 | public Result argumentInvalidException(MethodArgumentNotValidException ex) { 38 | log.warn("service exception:{}", ex.getMessage()); 39 | return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID, ex.getBindingResult().getFieldError().getDefaultMessage()); 40 | } 41 | 42 | @ExceptionHandler(value = {HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class}) 43 | public Result httpMessageConvertException(HttpMessageNotReadableException ex) { 44 | log.warn("http message convert exception:{}", ex.getMessage()); 45 | return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID, "数据解析错误:" + ex.getMessage()); 46 | } 47 | 48 | @ExceptionHandler(value = {MultipartException.class}) 49 | public Result uploadFileLimitException(MultipartException ex) { 50 | log.warn("upload file size limit:{}", ex.getMessage()); 51 | return Result.fail(SystemErrorType.UPLOAD_FILE_SIZE_LIMIT); 52 | } 53 | 54 | @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class}) 55 | @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) 56 | public Result notSupportedMethodException(HttpRequestMethodNotSupportedException ex) { 57 | log.warn("http request method not supported exception {}", ex.getMessage()); 58 | return Result.fail(SystemErrorType.METHOD_NOT_SUPPORTED); 59 | } 60 | 61 | @ExceptionHandler(value = {NoHandlerFoundException.class, NoResourceFoundException.class}) 62 | @ResponseStatus(HttpStatus.NOT_FOUND) 63 | public Result noHandlerFoundException(ServletException ex) { 64 | log.warn("No static resource exception:{}", ex.getMessage()); 65 | return Result.fail(SystemErrorType.RESOURCE_NOT_FOUND, ex.getMessage()); 66 | } 67 | 68 | @ExceptionHandler(value = {HttpMediaTypeNotSupportedException.class}) 69 | @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) 70 | public Result notSupportedMethodException(HttpMediaTypeNotSupportedException ex) { 71 | log.warn("http request media not supported exception {}", ex.getMessage()); 72 | return Result.fail(SystemErrorType.METHOD_NOT_SUPPORTED); 73 | } 74 | } -------------------------------------------------------------------------------- /opensabre-starter-webmvc/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | opensabre-framework 8 | io.github.opensabre 9 | ${revision} 10 | 11 | 12 | opensabre-starter-webmvc 13 | jar 14 | 15 | opensabre-starter-webmvc 16 | https://github.com/opensabre/ 17 | OpenSabre Web project for Spring Boot 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 23 | 17 24 | 17 25 | 26 | 27 | 28 | 29 | 30 | 31 | The Apache License, Version 2.0 32 | https://www.apache.org/licenses/LICENSE-2.0.txt 33 | 34 | 35 | 36 | 37 | 38 | zhoutaoo 39 | zhoutaoo@foxmail.com 40 | 41 | developer 42 | 43 | +8 44 | 45 | 46 | 47 | 48 | opensabre-web 49 | https://github.com/opensabre/opensabre-framework/opensabre-web 50 | scm:git:https://github.com/opensabre/opensabre-framework.git 51 | scm:git:https://github.com/opensabre/opensabre-framework.git 52 | 53 | 54 | 55 | 56 | 57 | io.github.opensabre 58 | opensabre-base-dependencies 59 | ${revision} 60 | pom 61 | import 62 | 63 | 64 | 65 | 66 | 67 | 68 | io.github.opensabre 69 | opensabre-web 70 | 71 | 72 | 73 | org.springframework 74 | spring-webmvc 75 | 76 | 77 | 78 | org.apache.commons 79 | commons-lang3 80 | 81 | 82 | 83 | com.fasterxml.jackson.core 84 | jackson-annotations 85 | 86 | 87 | 88 | jakarta.servlet 89 | jakarta.servlet-api 90 | provided 91 | 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-starter-test 96 | test 97 | 98 | 99 | 100 | org.junit.jupiter 101 | junit-jupiter 102 | test 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/test/java/io/github/opensabre/boot/sensitive/log/desensitizer/PasswordLogBackDesensitizerTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.LoggingEvent; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | class PasswordLogBackDesensitizerTest { 9 | 10 | @Test 11 | void desensitizing() { 12 | PasswordLogBackDesensitizer passwordLogBackDesensitizer = new PasswordLogBackDesensitizer(); 13 | LoggingEvent loggingEvent = new LoggingEvent(); 14 | assertEquals("password : *******", passwordLogBackDesensitizer.desensitizing(loggingEvent, "password : 1234567")); 15 | assertEquals("password : *******", passwordLogBackDesensitizer.desensitizing(loggingEvent, "password : 1234567")); 16 | assertEquals("PASSWORD :*******", passwordLogBackDesensitizer.desensitizing(loggingEvent, "PASSWORD :1234567")); 17 | assertEquals("password:************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "password:1qaz@WSXsdff")); 18 | assertEquals("password=************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "password=1qaz@WSXsdff")); 19 | assertEquals("password>************ , password<************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "password>1qaz@WSXsdff , password<123456789012")); 20 | assertEquals("Password:************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "Password:1qaz@WSXsdff")); 21 | assertEquals("PASSWORD:************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "PASSWORD:1qaz@WSXsdff")); 22 | assertEquals("PASSWD:************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "PASSWD:1qaz@WSXsdff")); 23 | assertEquals("pass:************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "pass:1qaz@WSXsdff")); 24 | assertEquals("PASS:************", passwordLogBackDesensitizer.desensitizing(loggingEvent, "PASS:1qaz@WSXsdff")); 25 | assertEquals("key:********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "key:12345678")); 26 | assertEquals("KEY:********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "KEY:12345678")); 27 | assertEquals("TOKEN:********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "TOKEN:12345678")); 28 | assertEquals("Token:********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "Token:12345678")); 29 | assertEquals("credential:********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "credential:12345678")); 30 | assertEquals("secret:********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "secret:12345678")); 31 | assertEquals("secret:*********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "secret:12345678=")); 32 | assertEquals("secret: *********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "secret: 12345678=")); 33 | assertEquals("Secret : *********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "Secret : 12345678=")); 34 | assertEquals("Secret : ***********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "Secret : \"12345678=\"")); 35 | assertEquals("Secret is *********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "Secret is 12345678=")); 36 | assertEquals("Secret IS *********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "Secret IS 12345678=")); 37 | assertEquals("this a Secret : ********", passwordLogBackDesensitizer.desensitizing(loggingEvent, "this a Secret : 12345678")); 38 | assertEquals("this a Secret : ******** please change it", passwordLogBackDesensitizer.desensitizing(loggingEvent, "this a Secret : 12345678 please change it")); 39 | assertEquals("this a Secret : ********* please change it", passwordLogBackDesensitizer.desensitizing(loggingEvent, "this a Secret : 12345678, please change it")); 40 | assertEquals("this a Secret : ********* please change it. passwd:******", passwordLogBackDesensitizer.desensitizing(loggingEvent, "this a Secret : 12345678, please change it. passwd:123456")); 41 | } 42 | } -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | opensabre 8 | 9 | 10 | /> 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | debug 29 | 30 | 31 | ${CONSOLE_LOG_PATTERN} 32 | UTF-8 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ${log.path}/opensabre.log 41 | 42 | 43 | ${FILE_LOG_PATTERN} 44 | UTF-8 45 | 46 | 47 | 48 | 49 | ${log.path}/opensabre-%d{yyyy-MM-dd}.%i.log 50 | 51 | 100MB 52 | 53 | 15 54 | 55 | 56 | 68 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/main/java/io/github/opensabre/boot/sensitive/rule/DefaultSensitiveRule.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.rule; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | /** 6 | * 简单的脱敏策略 7 | * 8 | * @author zhoutaoo 9 | */ 10 | @SuppressWarnings("unused") 11 | public enum DefaultSensitiveRule implements SensitiveRule { 12 | /** 13 | * 自定义 14 | */ 15 | CUSTOM("custom", 0, 0), 16 | /** 17 | * 账号类脱敏策略 18 | */ 19 | ACCOUNT_NO("accountNo", 3, 2), 20 | /** 21 | * 姓名类脱敏策略 22 | */ 23 | NAME("name", "((?i)name|姓名)\\s*(?:((?i)is)|[::=<>]+)\\s*([\\u4e00-\\u9fa5][\\u4e00-\\u9fa5]{1,5})", 1, 0), 24 | /** 25 | * 身份证号类脱敏策略 26 | */ 27 | ID_CARD("idCard", "(\\d{6})(19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[1-2]\\d|3[0-1])(\\d{3})((?i)x|[0-9])", 6, 2), 28 | /** 29 | * 银行卡号 30 | */ 31 | BANK_CARD("bankCard", "([3-6]\\d{3})(\\d{8,12})(\\d{4})", 4, 3), 32 | /** 33 | * 车牌号 34 | */ 35 | CAR_LICENSE("carLicense", "(([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-Z](([0-9]{5}[A-HJK])|([A-HJK]([A-HJ-NP-Z0-9])[0-9]{4})))|([京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼][A-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳使领]))", 2, 2), 36 | /** 37 | * 邮箱类脱敏策略 38 | */ 39 | EMAIL("email", "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}", 2, 7), 40 | /** 41 | * 手机号码类脱敏策略 42 | */ 43 | MOBILE("mobile", "(13[0-9]|14[01456789]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])(\\d{8})", 3, 4), 44 | /** 45 | * 中国固定电话 46 | */ 47 | PHONE("phone", "(0[1-9]\\d{1,2})[-]?\\d{7,8}$", 3, 2), 48 | /** 49 | * 密码 50 | */ 51 | PASSWORD("password", "((?i)password|pass|passwd|secret|key|credential|token)\\s*(?:((?i)is)|[::=<>]+)\\s*(\\S+)", 0, 0), 52 | /** 53 | * 中国住址类 54 | */ 55 | ADDRESS("address", "([\\u4e00-\\u9fa5]{2,5}[省|市|自治区|行政区])([\\u4e00-\\u9fa5]{1,}[市|县|区|地区|自治州|行政单位])([\\u4e00-\\u9fa5]{1,}[市|县|区|镇|地区|市辖区|旗|岛|路|街道|巷子|海域])(\\S*)", 2, 4); 56 | /** 57 | * 规则名称 58 | */ 59 | private final String category; 60 | /** 61 | * 正则表达式,日志脱敏使用 62 | */ 63 | private final Pattern pattern; 64 | /** 65 | * 保留前缀个数 66 | */ 67 | private final int retainPrefixCount; 68 | /** 69 | * 保留后缀个数 70 | */ 71 | private final int retainSuffixCount; 72 | /** 73 | * 用于替代明文的 密文字符 74 | */ 75 | private char replaceChar = '*'; 76 | 77 | DefaultSensitiveRule(String category, String pattern, int retainPrefixCount, int retainSuffixCount, char replaceChar) { 78 | this.category = category; 79 | this.pattern = Pattern.compile(pattern); 80 | this.retainPrefixCount = retainPrefixCount; 81 | this.retainSuffixCount = retainSuffixCount; 82 | this.replaceChar = replaceChar; 83 | } 84 | 85 | DefaultSensitiveRule(String category, String pattern, int retainPrefixCount, int retainSuffixCount) { 86 | this.category = category; 87 | this.pattern = Pattern.compile(pattern); 88 | this.retainPrefixCount = retainPrefixCount; 89 | this.retainSuffixCount = retainSuffixCount; 90 | } 91 | 92 | DefaultSensitiveRule(String category, int retainPrefixCount, int retainSuffixCount) { 93 | this.category = category; 94 | this.pattern = Pattern.compile("\\*"); 95 | this.retainPrefixCount = retainPrefixCount; 96 | this.retainSuffixCount = retainSuffixCount; 97 | } 98 | 99 | DefaultSensitiveRule(String category, String pattern) { 100 | this.category = category; 101 | this.pattern = Pattern.compile(pattern); 102 | this.retainPrefixCount = 0; 103 | this.retainSuffixCount = 0; 104 | } 105 | 106 | @Override 107 | public String category() { 108 | return this.category; 109 | } 110 | 111 | @Override 112 | public Pattern pattern() { 113 | return this.pattern; 114 | } 115 | 116 | @Override 117 | public int retainPrefixCount() { 118 | return this.retainPrefixCount; 119 | } 120 | 121 | @Override 122 | public int retainSuffixCount() { 123 | return this.retainSuffixCount; 124 | } 125 | 126 | @Override 127 | public char replaceChar() { 128 | return this.replaceChar; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/web/exception/DefaultGlobalExceptionHandlerAdvice.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.web.exception; 2 | 3 | import io.github.opensabre.common.core.entity.vo.Result; 4 | import io.github.opensabre.common.core.exception.BaseException; 5 | import io.github.opensabre.common.core.exception.SystemErrorType; 6 | import jakarta.servlet.ServletException; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.core.annotation.Order; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.http.converter.HttpMessageNotReadableException; 11 | import org.springframework.web.HttpMediaTypeNotSupportedException; 12 | import org.springframework.web.HttpRequestMethodNotSupportedException; 13 | import org.springframework.web.bind.MethodArgumentNotValidException; 14 | import org.springframework.web.bind.MissingServletRequestParameterException; 15 | import org.springframework.web.bind.annotation.ExceptionHandler; 16 | import org.springframework.web.bind.annotation.ResponseStatus; 17 | import org.springframework.web.bind.annotation.RestControllerAdvice; 18 | import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; 19 | import org.springframework.web.multipart.MultipartException; 20 | import org.springframework.web.servlet.NoHandlerFoundException; 21 | import org.springframework.web.servlet.resource.NoResourceFoundException; 22 | 23 | /** 24 | * 默认全局异常处理类 25 | */ 26 | @Slf4j 27 | @Order 28 | @RestControllerAdvice 29 | public class DefaultGlobalExceptionHandlerAdvice { 30 | 31 | @ExceptionHandler(value = {MissingServletRequestParameterException.class}) 32 | public Result missingServletRequestParameterException(MissingServletRequestParameterException ex) { 33 | log.warn("missing servlet request parameter exception:{}", ex.getMessage()); 34 | return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID); 35 | } 36 | 37 | @ExceptionHandler(value = {MethodArgumentNotValidException.class}) 38 | public Result argumentInvalidException(MethodArgumentNotValidException ex) { 39 | log.warn("service exception:{}", ex.getMessage()); 40 | return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID, ex.getBindingResult().getFieldError().getDefaultMessage()); 41 | } 42 | 43 | @ExceptionHandler(value = {HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class}) 44 | public Result httpMessageConvertException(HttpMessageNotReadableException ex) { 45 | log.warn("http message convert exception:{}", ex.getMessage()); 46 | return Result.fail(SystemErrorType.ARGUMENT_NOT_VALID, "数据解析错误:" + ex.getMessage()); 47 | } 48 | 49 | @ExceptionHandler(value = {MultipartException.class}) 50 | public Result uploadFileLimitException(MultipartException ex) { 51 | log.warn("upload file size limit:{}", ex.getMessage()); 52 | return Result.fail(SystemErrorType.UPLOAD_FILE_SIZE_LIMIT); 53 | } 54 | 55 | @ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class}) 56 | @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) 57 | public Result notSupportedMethodException(HttpRequestMethodNotSupportedException ex) { 58 | log.warn("http request method not supported exception {}", ex.getMessage()); 59 | return Result.fail(SystemErrorType.METHOD_NOT_SUPPORTED); 60 | } 61 | 62 | @ExceptionHandler(value = {NoHandlerFoundException.class, NoResourceFoundException.class}) 63 | @ResponseStatus(HttpStatus.NOT_FOUND) 64 | public Result noHandlerFoundException(ServletException ex) { 65 | log.warn("No static resource exception:{}", ex.getMessage()); 66 | return Result.fail(SystemErrorType.RESOURCE_NOT_FOUND, ex.getMessage()); 67 | } 68 | 69 | @ExceptionHandler(value = {HttpMediaTypeNotSupportedException.class}) 70 | @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) 71 | public Result notSupportedMethodException(HttpMediaTypeNotSupportedException ex) { 72 | log.warn("http request media not supported exception {}", ex.getMessage()); 73 | return Result.fail(SystemErrorType.METHOD_NOT_SUPPORTED); 74 | } 75 | 76 | @ExceptionHandler(value = {BaseException.class}) 77 | public Result baseException(BaseException ex) { 78 | log.error("base exception:{}", ex.getMessage()); 79 | return Result.fail(ex.getErrorType()); 80 | } 81 | 82 | @ExceptionHandler(value = {Exception.class, Throwable.class}) 83 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 84 | public Result exception(Throwable ex) { 85 | log.error("exception: ", ex); 86 | return Result.fail(); 87 | } 88 | } -------------------------------------------------------------------------------- /opensabre-web/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | 7 | opensabre-framework 8 | io.github.opensabre 9 | ${revision} 10 | 11 | 12 | opensabre-web 13 | jar 14 | 15 | opensabre-web 16 | https://github.com/opensabre/ 17 | OpenSabre Web project for Spring Boot 18 | 19 | 20 | UTF-8 21 | UTF-8 22 | 23 | 17 24 | 17 25 | 26 | 27 | 28 | 29 | 30 | 31 | The Apache License, Version 2.0 32 | https://www.apache.org/licenses/LICENSE-2.0.txt 33 | 34 | 35 | 36 | 37 | 38 | zhoutaoo 39 | zhoutaoo@foxmail.com 40 | 41 | developer 42 | 43 | +8 44 | 45 | 46 | 47 | 48 | opensabre-web 49 | https://github.com/opensabre/opensabre-framework/opensabre-web 50 | scm:git:https://github.com/opensabre/opensabre-framework.git 51 | scm:git:https://github.com/opensabre/opensabre-framework.git 52 | 53 | 54 | 55 | 56 | 57 | io.github.opensabre 58 | opensabre-base-dependencies 59 | ${revision} 60 | pom 61 | import 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | io.swagger.core.v3 70 | swagger-annotations-jakarta 71 | 72 | 73 | 74 | org.projectlombok 75 | lombok 76 | 77 | 78 | org.slf4j 79 | slf4j-api 80 | 81 | 82 | 83 | org.springframework 84 | spring-webmvc 85 | 86 | 87 | 88 | org.apache.commons 89 | commons-pool2 90 | 91 | 92 | org.apache.commons 93 | commons-lang3 94 | 95 | 96 | cn.hutool 97 | hutool-core 98 | 99 | 100 | 101 | com.google.guava 102 | guava 103 | 104 | 105 | 106 | com.fasterxml.jackson.core 107 | jackson-annotations 108 | 109 | 110 | com.fasterxml.jackson.core 111 | jackson-databind 112 | 113 | 114 | com.fasterxml.jackson.datatype 115 | jackson-datatype-jsr310 116 | 117 | 118 | 119 | jakarta.validation 120 | jakarta.validation-api 121 | 122 | 123 | 124 | jakarta.servlet 125 | jakarta.servlet-api 126 | provided 127 | 128 | 129 | 130 | org.springframework.boot 131 | spring-boot-starter-test 132 | test 133 | 134 | 135 | 136 | org.junit.jupiter 137 | junit-jupiter 138 | test 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # CLAUDE.md 2 | 3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 | 5 | ## Project Overview 6 | 7 | Opensabre is a microservices development platform based on Spring Cloud 2023, integrating Spring Security, Spring Cloud Alibaba, and other components. It provides foundational RBAC permission management, authorization authentication, gateway management, service governance, audit logging, and other system management applications. 8 | 9 | ## Build and Development Commands 10 | 11 | ### Building the Project 12 | ```bash 13 | # Build all modules 14 | mvn clean install 15 | 16 | # Build with tests 17 | mvn clean install -DskipTests=false 18 | 19 | # Skip tests for faster builds 20 | mvn clean install -DskipTests=true 21 | 22 | # Deploy to Maven Central (requires deploy profile) 23 | mvn clean deploy -Pdeploy 24 | ``` 25 | 26 | ### Testing 27 | ```bash 28 | # Run all tests 29 | mvn test 30 | 31 | # Run tests for specific module 32 | cd opensabre-web && mvn test 33 | 34 | # Run single test class 35 | mvn test -Dtest=UserContextHolderTest 36 | ``` 37 | 38 | ### Code Quality 39 | ```bash 40 | # Generate JavaDoc 41 | mvn javadoc:javadoc 42 | 43 | # Generate flattened POM (for deployment) 44 | mvn flatten:flatten 45 | ``` 46 | 47 | ## Architecture and Module Structure 48 | 49 | ### Core Modules 50 | 51 | - **opensabre-base-dependencies**: Central dependency management with Spring Boot 3.4.1, Spring Cloud 2024.0.0, and Spring Cloud Alibaba 2023.0.3.2 52 | - **opensabre-web**: Core web utilities including exception handling, validation, and entity converters 53 | - **opensabre-starter-boot**: Auto-configuration for global exception handling, response wrapping, and sensitive data processing 54 | - **opensabre-starter-rpc**: RPC components with OpenFeign configuration, load balancing, and Sentinel integration 55 | - **opensabre-starter-persistence**: MyBatis Plus integration with base entity classes and meta object handlers 56 | - **opensabre-starter-cache**: Redis caching with JetCache implementation 57 | - **opensabre-starter-config**: Configuration management 58 | - **opensabre-starter-register**: Service discovery and registration (Nacos) 59 | - **opensabre-starter-eda**: Event-driven architecture components 60 | - **opensabre-starter-webmvc**: Web MVC utilities and exception handling 61 | - **opensabre-test**: Testing utilities 62 | 63 | ### Key Architectural Patterns 64 | 65 | #### 1. Unified Response Format 66 | All REST APIs return `Result` objects with standardized structure: 67 | - `code`: "000000" for success, other codes for errors 68 | - `mesg`: Human-readable message 69 | - `time`: Timestamp in UTC format 70 | - `data`: Response payload (nullable) 71 | 72 | #### 2. Entity Layering 73 | - **BasePo**: Base persistence object with audit fields (createdBy, createdTime, updatedBy, updatedTime) 74 | - **BaseVo**: Base view object for API responses 75 | - **BaseForm**: Base form object for API requests 76 | - **BaseQueryForm**: Base query form for search operations 77 | 78 | #### 3. Exception Handling 79 | - Global exception handling via `DefaultGlobalExceptionHandlerAdvice` 80 | - Custom exception hierarchy with `BaseException` and `ServiceException` 81 | - Error type system with `ErrorType` and `SystemErrorType` 82 | 83 | #### 4. Sensitive Data Processing 84 | - Log desensitization for sensitive fields (passwords, names) 85 | - REST API response desensitization 86 | - Configurable desensitization strategies 87 | 88 | #### 5. Service Communication 89 | - OpenFeign with custom load balancing (`OpensabreLoadBalancer`) 90 | - Header propagation via `FeignHeaderInterceptor` 91 | - Sentinel integration for circuit breaking 92 | 93 | ## Development Guidelines 94 | 95 | ### Adding New Modules 96 | 1. Create module directory under root 97 | 2. Add module to parent POM's `` section 98 | 3. Define dependencies in `opensabre-base-dependencies/pom.xml` 99 | 4. Use `@AutoConfiguration` for Spring Boot auto-configuration classes 100 | 101 | ### Creating REST Endpoints 102 | - Extend from `BasePo` for persistence entities 103 | - Use `Result` for all API responses 104 | - Implement proper exception handling 105 | - Follow the entity conversion pattern: `Po -> Vo` 106 | 107 | ### Database Operations 108 | - Extend `BasePo` for entity classes 109 | - Use MyBatis Plus for ORM 110 | - Implement `PoMetaObjectHandler` for audit field population 111 | 112 | ### Configuration 113 | - Use YAML configuration files with `YamlPropertyLoaderFactory` 114 | - Place configuration in `src/main/resources/` 115 | - Follow naming convention: `opensabre-{module}.yml` 116 | 117 | ## Technology Stack 118 | 119 | - **Java 17**: Primary development language 120 | - **Spring Boot 3.4.1**: Application framework 121 | - **Spring Cloud 2024.0.0**: Microservices framework 122 | - **Spring Cloud Alibaba 2023.0.3.2**: Alibaba cloud components 123 | - **MyBatis Plus 3.5.5**: ORM framework 124 | - **JetCache 2.7.7**: Multi-level caching 125 | - **Knife4j 4.5.0**: API documentation 126 | - **Hutool 5.8.35**: Utility library 127 | - **JUnit 5**: Testing framework 128 | 129 | ## Deployment 130 | 131 | ### Docker Support 132 | - Dockerfile Maven plugin for image building 133 | - Jib Maven plugin for containerization 134 | - Base image: `eclipse-temurin:21-jre-alpine` 135 | 136 | ### Maven Central Publishing 137 | - Uses `central-publishing-maven-plugin` 138 | - Requires `deploy` profile activation 139 | - Automatic source and JavaDoc packaging 140 | 141 | ## Testing Strategy 142 | 143 | - Unit tests in `src/test/java` directories 144 | - Integration tests for configuration validation 145 | - Test utilities in `opensabre-test` module 146 | - Use `@AutoConfiguration` for testing configuration classes -------------------------------------------------------------------------------- /opensabre-web/src/main/java/io/github/opensabre/common/core/entity/vo/Result.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.common.core.entity.vo; 2 | 3 | import cn.hutool.core.date.DatePattern; 4 | import com.fasterxml.jackson.annotation.JsonFormat; 5 | import com.fasterxml.jackson.annotation.JsonIgnore; 6 | import com.fasterxml.jackson.annotation.JsonInclude; 7 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 8 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 9 | import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; 10 | import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; 11 | import io.github.opensabre.common.core.exception.BaseException; 12 | import io.github.opensabre.common.core.exception.ErrorType; 13 | import io.github.opensabre.common.core.exception.SystemErrorType; 14 | import io.swagger.v3.oas.annotations.media.Schema; 15 | import lombok.Getter; 16 | 17 | import java.time.LocalDateTime; 18 | 19 | /** 20 | * Rest统一返回数据结构 21 | */ 22 | @Schema(description = "rest请求的返回模型,所有rest正常都返回该类的对象") 23 | @Getter 24 | public class Result { 25 | /** 26 | * 处理成功的常量代码 27 | */ 28 | public static final String SUCCESSFUL_CODE = "000000"; 29 | /** 30 | * 处理成功的默认提示信息 31 | */ 32 | public static final String SUCCESSFUL_MESG = "处理成功"; 33 | /** 34 | * 统一报文中code代码,标明异常类型 35 | */ 36 | @Schema(title = "处理结果code", description = "处理结果,000000为成功", requiredMode = Schema.RequiredMode.REQUIRED, example="000000") 37 | private final String code; 38 | /** 39 | * 统一报文中提示信息 40 | */ 41 | @Schema(title = "处理结果描述信息", description = "对code的详细说明和描述", example = "成功") 42 | private final String mesg; 43 | /** 44 | * 统一报文中报文生成时间 45 | */ 46 | @Schema(title = "请求结果生成时间戳", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-01-01T20:30:00.123Z") 47 | @JsonFormat(pattern = DatePattern.UTC_MS_PATTERN) 48 | @JsonSerialize(using = LocalDateTimeSerializer.class) 49 | @JsonDeserialize(using = LocalDateTimeDeserializer.class) 50 | private final LocalDateTime time; 51 | /** 52 | * 统一报文中数据部分,支持泛型 53 | */ 54 | @Schema(title = "处理结果数据信息", description = "可能为字符串、数组、对象等", requiredMode = Schema.RequiredMode.REQUIRED, example = "上海市") 55 | @JsonInclude(JsonInclude.Include.NON_NULL) 56 | private T data; 57 | 58 | /** 59 | * 默认构建方法,无参时默认为成功报文 60 | */ 61 | public Result() { 62 | this.code = SUCCESSFUL_CODE; 63 | this.mesg = SUCCESSFUL_MESG; 64 | this.time = LocalDateTime.now(); 65 | } 66 | 67 | /** 68 | * 通过ErrorType构建Result 69 | * 70 | * @param errorType 错误类型 71 | */ 72 | public Result(ErrorType errorType) { 73 | this.code = errorType.getCode(); 74 | this.mesg = errorType.getMesg(); 75 | this.time = LocalDateTime.now(); 76 | } 77 | 78 | /** 79 | * 通过ErrorType和data数据构建Result 80 | * 81 | * @param errorType 错误类型 82 | * @param data 错误数据 83 | */ 84 | public Result(ErrorType errorType, T data) { 85 | this(errorType); 86 | this.data = data; 87 | } 88 | 89 | /** 90 | * 内部使用,用于构造成功的结果 91 | * 92 | * @param code 错误码 93 | * @param mesg 错误信息 94 | * @param data 错误数据对象 95 | */ 96 | private Result(String code, String mesg, T data) { 97 | this.code = code; 98 | this.mesg = mesg; 99 | this.data = data; 100 | this.time = LocalDateTime.now(); 101 | } 102 | 103 | /** 104 | * 快速创建成功结果并返回结果数据 105 | * 106 | * @param data 错误数据对象 107 | * @return Result 108 | */ 109 | public static Result success(Object data) { 110 | return new Result<>(SUCCESSFUL_CODE, SUCCESSFUL_MESG, data); 111 | } 112 | 113 | /** 114 | * 快速创建成功结果 115 | * 116 | * @return Result 117 | */ 118 | public static Result success() { 119 | return success(null); 120 | } 121 | 122 | /** 123 | * 系统异常类没有返回数据 124 | * 125 | * @return Result 126 | */ 127 | public static Result fail() { 128 | return new Result(SystemErrorType.SYSTEM_ERROR); 129 | } 130 | 131 | /** 132 | * 系统异常类并返回结果数据 133 | * 134 | * @param data 错误数据对象 135 | * @return Result 136 | */ 137 | public static Result fail(Object data) { 138 | return new Result<>(SystemErrorType.SYSTEM_ERROR, data); 139 | } 140 | 141 | /** 142 | * 系统异常类没有返回数据 143 | * 144 | * @param baseException 异常类参数 145 | * @return Result 146 | */ 147 | public static Result fail(BaseException baseException) { 148 | return fail(baseException, null); 149 | } 150 | 151 | /** 152 | * 系统异常类并返回结果数据 153 | * 154 | * @param data 错误数据对象 155 | * @param baseException 异常对象 156 | * @return Result 157 | */ 158 | public static Result fail(BaseException baseException, Object data) { 159 | return new Result<>(baseException.getErrorType(), data); 160 | } 161 | 162 | /** 163 | * 系统异常类并返回结果数据 164 | * 165 | * @param errorType 错误类型 166 | * @param data 错误数据 167 | * @return Result 168 | */ 169 | public static Result fail(ErrorType errorType, Object data) { 170 | return new Result<>(errorType, data); 171 | } 172 | 173 | /** 174 | * 系统异常类并返回结果数据 175 | * 176 | * @param errorType 错误类型 177 | * @return Result 178 | */ 179 | public static Result fail(ErrorType errorType) { 180 | return Result.fail(errorType, null); 181 | } 182 | 183 | /** 184 | * 成功code=000000 185 | * 186 | * @return true/false 187 | */ 188 | @JsonIgnore 189 | public boolean isSuccess() { 190 | return SUCCESSFUL_CODE.equals(this.code); 191 | } 192 | 193 | /** 194 | * 失败 195 | * 196 | * @return true/false 197 | */ 198 | @JsonIgnore 199 | public boolean isFail() { 200 | return !isSuccess(); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /opensabre-starter-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | opensabre-framework 9 | io.github.opensabre 10 | ${revision} 11 | 12 | 13 | opensabre-starter-boot 14 | jar 15 | 16 | opensabre-starter-boot 17 | Opensabre Boot Starter 18 | 19 | 20 | 1.5.13 21 | 2.7.0 22 | 23 | 24 | 25 | 26 | 27 | io.github.opensabre 28 | opensabre-base-dependencies 29 | ${revision} 30 | pom 31 | import 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.springframework.cloud 40 | spring-cloud-starter-bootstrap 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-undertow 46 | 47 | 48 | 49 | io.github.opensabre 50 | opensabre-web 51 | 52 | 53 | 54 | org.springframework.boot 55 | spring-boot-starter-actuator 56 | 57 | 58 | ch.qos.logback 59 | logback-core 60 | 61 | 62 | 63 | 64 | 65 | io.micrometer 66 | micrometer-tracing-bridge-otel 67 | 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-starter-validation 72 | 73 | 74 | 75 | com.github.ulisesbocchio 76 | jasypt-spring-boot-starter 77 | 78 | 79 | 80 | com.github.xiaoymin 81 | knife4j-openapi3-jakarta-spring-boot-starter 82 | 83 | 84 | org.springframework.boot 85 | spring-boot-configuration-processor 86 | 87 | 88 | org.springdoc 89 | springdoc-openapi-starter-webmvc-ui 90 | 91 | 92 | 93 | 94 | ch.qos.logback 95 | logback-core 96 | ${logback-core.version} 97 | 98 | 99 | org.springdoc 100 | springdoc-openapi-starter-webmvc-ui 101 | ${springdoc-openapi-starter.version} 102 | 103 | 104 | 105 | org.springframework.boot 106 | spring-boot-configuration-processor 107 | true 108 | 109 | 110 | cn.hutool 111 | hutool-setting 112 | 113 | 114 | 115 | org.springframework.boot 116 | spring-boot-devtools 117 | runtime 118 | true 119 | 120 | 121 | 122 | org.projectlombok 123 | lombok 124 | 125 | 126 | 127 | org.springframework.boot 128 | spring-boot-test 129 | test 130 | 131 | 132 | io.github.opensabre 133 | opensabre-test 134 | test 135 | 136 | 137 | org.springframework 138 | spring-test 139 | test 140 | 141 | 142 | org.aspectj 143 | aspectjweaver 144 | 145 | 146 | -------------------------------------------------------------------------------- /opensabre-starter-boot/src/test/java/io/github/opensabre/boot/sensitive/log/desensitizer/RegxLogBackDesensitizerTest.java: -------------------------------------------------------------------------------- 1 | package io.github.opensabre.boot.sensitive.log.desensitizer; 2 | 3 | import ch.qos.logback.classic.spi.LoggingEvent; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.extension.ExtendWith; 6 | import org.springframework.test.context.ContextConfiguration; 7 | import org.springframework.test.context.TestPropertySource; 8 | import org.springframework.test.context.junit.jupiter.SpringExtension; 9 | 10 | import jakarta.annotation.Resource; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | @ExtendWith(SpringExtension.class) 15 | @ContextConfiguration(classes = RegxLogBackDesensitizer.class) 16 | @TestPropertySource(properties = {"opensabre.sensitive.log.rules=idCard,mobile,phone,name,email,address,carLicense"}) 17 | class RegxLogBackDesensitizerTest { 18 | 19 | @Resource 20 | private RegxLogBackDesensitizer regxLogBackDesensitizer; 21 | 22 | @Test 23 | void desensitizingMobile() { 24 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "mobile=13612345678"); 25 | assertEquals("mobile=136****5678", desensitizing); 26 | } 27 | 28 | @Test 29 | void desensitizingPhone4WithSeparator() { 30 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=0571-12345678"); 31 | assertEquals("phone=057********78", desensitizing); 32 | } 33 | 34 | @Test 35 | void desensitizingPhone4WithSeparator7() { 36 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=0571-1234567"); 37 | assertEquals("phone=057*******67", desensitizing); 38 | } 39 | 40 | @Test 41 | void desensitizingPhone4() { 42 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=057112345678"); 43 | assertEquals("phone=057*******78", desensitizing); 44 | } 45 | 46 | @Test 47 | void desensitizingPhone47() { 48 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=05711234567"); 49 | assertEquals("phone=057******67", desensitizing); 50 | } 51 | 52 | @Test 53 | void desensitizingPhone3() { 54 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=02912345678"); 55 | assertEquals("phone=029******78", desensitizing); 56 | } 57 | 58 | @Test 59 | void desensitizingPhone37() { 60 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=0291234567"); 61 | assertEquals("phone=029*****67", desensitizing); 62 | } 63 | 64 | @Test 65 | void desensitizingPhone3WithSeparator() { 66 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=029-12345678"); 67 | assertEquals("phone=029*******78", desensitizing); 68 | } 69 | 70 | @Test 71 | void desensitizingPhone3WithSeparator7() { 72 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "phone=029-1234567"); 73 | assertEquals("phone=029******67", desensitizing); 74 | } 75 | 76 | @Test 77 | void desensitizingIdCardx() { 78 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "id=61231019900909101x"); 79 | assertEquals("id=612310**********1x", desensitizing); 80 | } 81 | 82 | @Test 83 | void desensitizingIdCardX() { 84 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "id=61231019900909101X"); 85 | assertEquals("id=612310**********1X", desensitizing); 86 | } 87 | 88 | @Test 89 | void desensitizingIdCardNum() { 90 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "id=612310199009091011"); 91 | assertEquals("id=612310**********11", desensitizing); 92 | } 93 | 94 | @Test 95 | void desensitizingEmail() { 96 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "email:123@123.com"); 97 | assertEquals("email:12**123.com", desensitizing); 98 | } 99 | 100 | @Test 101 | void desensitizingEmailShort() { 102 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "email:123@qq.cn"); 103 | assertEquals("email:123@qq.cn", desensitizing); 104 | } 105 | 106 | @Test 107 | void desensitizingAddressCity() { 108 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "上海市浦东新区上丰路1288号"); 109 | assertEquals("上海*********288号", desensitizing); 110 | } 111 | 112 | @Test 113 | void desensitizingAddressProvince() { 114 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "陕西省汉中市南郑区汉中街道100号"); 115 | assertEquals("陕西***********100号", desensitizing); 116 | } 117 | 118 | @Test 119 | void desensitizingAddressHK() { 120 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "中国香港特别行政区大埔区普门路638号"); 121 | assertEquals("中国香************638号", desensitizing); 122 | } 123 | 124 | @Test 125 | void desensitizingAddressZhou() { 126 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "广西壮族自治区南宁市在四大区上江路10号502室"); 127 | assertEquals("广西******************502室", desensitizing); 128 | } 129 | 130 | @Test 131 | void desensitizingAddressTW() { 132 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "中国台湾省台北市新竹县桃源村5号天下花园1幢103室"); 133 | assertEquals("中国********************103室", desensitizing); 134 | } 135 | 136 | @Test 137 | void desensitizingCarLicense() { 138 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "粤AA12345"); 139 | assertEquals("粤A****45", desensitizing); 140 | } 141 | 142 | @Test 143 | void desensitizingCarLicenseBus() { 144 | String desensitizing = regxLogBackDesensitizer.desensitizing(new LoggingEvent(), "沪FE0708"); 145 | assertEquals("沪F***08", desensitizing); 146 | } 147 | } --------------------------------------------------------------------------------