init commit

This commit is contained in:
beryl 2025-07-15 13:12:39 +08:00
parent 51c5f2a5f6
commit 3fe3f891da
29 changed files with 1760 additions and 23 deletions

59
.gitignore vendored
View File

@ -1,26 +1,41 @@
# ---> Java
# Compiled class file
*.class
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
# Log file
*.log
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
# BlueJ files
*.ctxt
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
## IDEA maven
/.flattened-pom.xml
/**/flattened-pom.xml
admin-application/.flattened-pom.xml
admin-application/logs/
admin-client/.flattened-pom.xml
admin-core/.flattened-pom.xml
admin-model/.flattened-pom.xml

82
Dockerfile Normal file
View File

@ -0,0 +1,82 @@
FROM openjdk:8
ARG JAR_FILE="./target/*.jar"
ARG APP_NAME="bi-gateway"
ARG SERVER_PORT=80
ARG PROFILES="dev"
ARG SPRING_CLOUD_SENTINEL_ENABLED="false"
ARG SPRING_CLOUD_SENTINEL_EAGER="false"
# license配置
ARG LICENSE_PATH="/opt/license/verifyinfo"
ARG LICENSE_REDIS_HOST="10.0.5.17:6679"
ARG LICENSE_REDIS_PWD="Do1admin@hr123"
ARG LICENSE_IP="10.0.5.17"
ARG NACOS_USERNAME="nacos"
ARG NACOS_PASSWORD="nacos"
# 内存配置
ENV JAVA_OPTS "-Xmx2048m -Xss256k"
#启动环境配置
ENV PROFILES $PROFILES
#工作路径
ENV WORK_PATH "/home"
#日志路径
ENV LOG_FILE "logs/app.log"
#服务端口
ENV SERVER_PORT $SERVER_PORT
# NACOS 配置
ENV NACOS_SERVER $NACOS_SERVER
ENV NACOS_NAMESPACE $NACOS_NAMESPACE
ENV NACOS_USERNAME $NACOS_USERNAME
ENV NACOS_PASSWORD $NACOS_PASSWORD
ENV LICENSE_PATH $LICENSE_PATH
ENV LICENSE_REDIS_HOST $LICENSE_REDIS_HOST
ENV LICENSE_REDIS_PWD $LICENSE_REDIS_PWD
ENV LICENSE_IP $LICENSE_IP
ENV LOGGIN_FILE $LOGGIN_FILE
# Sentinel配置
ENV SPRING_CLOUD_SENTINEL_ENABLED $SPRING_CLOUD_SENTINEL_ENABLED
ENV SPRING_CLOUD_SENTINEL_EAGER $SPRING_CLOUD_SENTINEL_EAGER
ENV SPRING_CLOUD_SENTINEL_TRANSPORT_PORT $SPRING_CLOUD_SENTINEL_TRANSPORT_PORT
ENV SPRING_CLOUD_SENTINEL_TRANSPORT_DASHBOARD $SPRING_CLOUD_SENTINEL_TRANSPORT_DASHBOARD
#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
VOLUME /tmp
EXPOSE ${SERVER_PORT}
#WITH_SKYWALKING# ADD skywalking-agent.tar.gz /lib/
COPY ${JAR_FILE} ${WORK_PATH}/app.jar
RUN sh -c 'touch ${WORK_PATH}/app.jar'
#ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom --server.port=$SERVER_PORT -Dapollo.meta=$APOLLO_META -Dapp.id=$APOLLO_ID -Dspring.profiles.active=$PROFILES -jar $WORK_PATH/app.jar " ]
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom \
-Dserver.port=$SERVER_PORT \
-Dspring.cloud.nacos.config.server-addr=$NACOS_SERVER \
-Dspring.cloud.nacos.config.namespace=$NACOS_NAMESPACE \
-Dspring.cloud.nacos.config.username=$NACOS_USERNAME \
-Dspring.cloud.nacos.config.password=$NACOS_PASSWORD \
-Dspring.cloud.nacos.discovery.server-addr=$NACOS_SERVER \
-Dspring.cloud.nacos.discovery.username=$NACOS_USERNAME \
-Dspring.cloud.nacos.discovery.password=$NACOS_PASSWORD \
-Dspring.cloud.nacos.discovery.namespace=$NACOS_NAMESPACE \
-Dspring.profiles.active=$PROFILES \
-Ddo1.license=$LICENSE_PATH \
-Dredis.host=$LICENSE_REDIS_HOST \
-Dredis.pwd=$LICENSE_REDIS_PWD \
-Dlicense.ip=$LICENSE_IP \
-Dlogging.file=$LOGGIN_FILE \
-Dspring.cloud.sentinel.enabled=$SPRING_CLOUD_SENTINEL_ENABLED \
-Dspring.cloud.sentinel.eager=$SPRING_CLOUD_SENTINEL_EAGER \
-Dspring.cloud.sentinel.transport.port=$SPRING_CLOUD_SENTINEL_TRANSPORT_PORT \
-Dspring.cloud.sentinel.transport.dashboard=$SPRING_CLOUD_SENTINEL_TRANSPORT_DASHBOARD \
-Duser.timezone=GMT+08 -jar $WORK_PATH/app.jar" ]

118
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,118 @@
pipeline {
agent {
node {
label labelName
}
}
stages {
stage('初始化') {
steps {
script {
echo """ ==== 构建信息 ====
应 用 名:$serverName
分 支:$gitBranch
gitkey$gitkey
harborkey: $harborkey
镜像版本:$DOCKER_TAG """
echo """ $params"""
}
}
}
stage('编译') {
steps {
script {
container(labelName) {
dir(serverName) {
checkout([
$class: 'GitSCM',
branches: [[name: "*/$gitBranch"]],
doGenerateSubmoduleConfigurations: false,
extensions: [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: true]],
submoduleCfg: [],
userRemoteConfigs: [[url: gitPath, credentialsId: gitkey]],
])
/** 打印基础信息 **/
sh " echo 打印基础信息 "
sh " pwd "
sh " ls "
sh " mvn clean install -e -Dmaven.test.skip=true "
}
}
}
}
}
stage('镜像打包') {
steps {
script {
container(labelName) {
dir(serverName) {
withCredentials([usernamePassword(passwordVariable: 'DOCKER_PASSWORD', usernameVariable: 'DOCKER_USERNAME', credentialsId: "harborkey",)]) {
sh 'echo "$DOCKER_PASSWORD" | docker login $DOCKER_REGISTRY -u "$DOCKER_USERNAME" --password-stdin'
sh """
cd admin-application
docker build -t $DOCKER_IMAGE --build-arg JAR_FILE=$filepath --build-arg APP_NAME=$serverName .
docker push $DOCKER_IMAGE
"""
}
}
}
}
}
}
stage('发布服务') {
steps {
container(labelName) {
script{
//查询服务是否已经创建,如果已经创建再次发布只更新镜像
def result = ""
def global_config = true
def license = true
def harborkey = true
//服务是否已经部署
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE'){
result = sh(returnStdout: true, script: "kubectl get deployment $serverName -n $namespace ").trim()
}
if(result==""){
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE'){
sh "helm delete $serverName -n $namespace "
}
sh "helm repo update "
sh "helm install --set image.repository=$DOCKER_REGISTRY,image.tag=$DOCKER_TAG,nameOverride=$serverName $serverName do1-chart/base-server --version=$CHART_VERSION -n $namespace "
}else{
sh "kubectl set image deploy $serverName *=$DOCKER_IMAGE -n $namespace"
}
}
}
}
}
}
environment {
dockerFile = "Dockerfile"
filepath = " ./target/**.jar "
labelName = "maven"
DOCKER_REGISTRY="$DOCKER_REGISTRY/$namespace/$serverName".replaceAll("//","/")
DOCKER_IMAGE="$DOCKER_REGISTRY:$DOCKER_TAG".replaceAll("//","/")
CHART_VERSION = "1.1.0"
}
// 说明: 参数初始化脚本,默认情况下是注释的,如果需要新增参数可以通过可视化界面添加也可以通过此脚本添加,一般用来初始化和调整顺序,注意,使用此代码片段添加参数,参数会在第一次构建生效,且会覆相同的参数值,以代码片段中的参数配置重新添加,意思就是如果初始化后,下面代码 parameters 没有 注释,那么每次运行后之前添加或修改的参数都会被下面的参数和默认值替换
parameters {
string(name: 'gitBranch', defaultValue: 'dev', description: '选择需要发布的分支')
string(name: 'gitPath', defaultValue: 'https://git.qiweioa.cn/bi-system/bi-admin.git', description: 'git 仓库地址')
string(name: 'serverName', defaultValue: 'bi-admin-dev', description: 'KubeSphere服务名')
string(name: 'namespace', defaultValue: 'bi-dev', description: '需要发布的k8s命名空间每个项目都应该有一个命名空间且在集群中唯一')
string(name: 'gitkey', defaultValue: 'denghuizhi', description: 'git仓库访问凭证配置在流水线访问凭证中gitkey为全局的配置在Jenkins中不需要手动创建模版中的值由主流程传递过来')
string(name: 'harborkey', defaultValue: 'harborkey', description: '镜像仓库访问凭证harborkey 为默认值,镜像仓库地址发生变更时需要修改此参数,自定义凭证需要在流水线凭证中创建')
string(name: 'DOCKER_REGISTRY', defaultValue: 'harbor.uat.do1.com.cn:6001/do1cloud/', description: '镜像仓库,默认值 harbor.uat.do1.com.cn/do1cloud/ 为公司全局镜像仓库如有需要可以自行更改如果使用了其他镜像仓库地址需要修改dockerkey的值')
string(name: 'DOCKER_TAG', defaultValue: 'dev', description: '镜像版本')
}
}

View File

@ -1,2 +1,18 @@
# bi-gateway
## 默认访问路径
- http://网关IP:端口/{nacos服务名}/{RequestMapping的路径},如下图
```shell
http://127.0.0.1/saas-dubbo-all-application/acc/user?id=2&test=aba
```
## 打印access log
```jvm
-Dreactor.netty.http.server.accessLogEnabled=true
```
## 参考资料
- [基础入门](https://spring.io/guides/gs/gateway/) 官方gateway使用示例
- [gateway官方文档](https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-discoveryclient-route-definition-locator)
12.4节 The DiscoveryClient Route Definition Locator
- [核心重要接口](https://blog.csdn.net/u010647035/article/details/84480626) RouteLocator、RouteDefinitionRouteLocator、DiscoveryClient、DiscoveryClientRouteDefinitionLocator
、重要配置类DiscoveryLocatorProperties、GatewayDiscoveryClientAutoConfiguration以及不同厂家的DiscoveryClient实现配置、RouteRefreshListener
- [路由转发过程](https://zhuanlan.zhihu.com/p/449990867) 通过handler利用RouteLocator的getRoutes实现路由转发
- [gateway性能优化](https://blog.csdn.net/weixin_42161936/article/details/123395773) 面向ISV的架构

3
buildDocker.sh Normal file
View File

@ -0,0 +1,3 @@
docker build -t harbor.uat.do1.com.cn:6001/do1cloud/bi-release/bi-gateway-release:0.5.0 .
docker login -u admin -p Harbor12345 harbor.uat.do1.com.cn:6001
docker push harbor.uat.do1.com.cn:6001/do1cloud/bi-release/bi-gateway-release:0.5.0

176
pom.xml Normal file
View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.com.do1.do1cloud.bi</groupId>
<artifactId>bi-denpendencies</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>cn.com.do1.do1cloud.bi</groupId>
<artifactId>bi-gateway</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<dqdp.license.version>2.1.0-SNAPSHOT</dqdp.license.version>
<spring-boot-maven-plugin.version>2.1.1.RELEASE</spring-boot-maven-plugin.version>
<project.parent.artifactId.version>2.2.1-SNAPSHOT</project.parent.artifactId.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.0.8</version>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/io.projectreactor.netty/reactor-netty-http -->
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-http</artifactId>
<version>1.0.29</version>
</dependency>
<dependency>
<groupId>io.projectreactor.netty</groupId>
<artifactId>reactor-netty-core</artifactId>
<version>1.0.29</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
<version>3.4.27</version>
</dependency>
<!--客户端负载均衡loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
<version>4.0.2</version>
</dependency>
<!--客户端依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--配置中心依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>cn.com.do1.do1cloud.bi</groupId>
<artifactId>bi-model</artifactId>
<version>${parent.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.16</version>
<exclusions>
<exclusion>
<artifactId>commons-compiler</artifactId>
<groupId>org.codehaus.janino</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>commons-compiler</artifactId>
<version>3.0.16</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<!--spring-cloud-alibaba 版本控制-->
<dependency>
<groupId>cn.com.do1.do1cloud.bi</groupId>
<artifactId>bi-denpendencies</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-maven-plugin.version}</version>
<configuration>
<fork>true</fork>
<mainClass>cn.com.do1.component.bi.gateway.GatewayApplication</mainClass>
<layoutFactory implementation="cn.com.do1.license.boot.DqdpLayoutFactory">
<licenseJar>dqdp-license-${dqdp.license.version}.jar</licenseJar>
</layoutFactory>
</configuration>
<dependencies>
<dependency>
<groupId>cn.com.do1</groupId>
<artifactId>dqdp-license</artifactId>
<version>${dqdp.license.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,18 @@
package cn.com.do1.component.bi.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication(exclude = GatewayDiscoveryClientAutoConfiguration.class)
@RestController
@EnableDiscoveryClient
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}

View File

@ -0,0 +1,21 @@
package cn.com.do1.component.bi.gateway.config.DynamicRoute;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
@RefreshScope
@Data
@Getter
@Setter
@ConfigurationProperties(prefix = "dynamic-routes")
public class DynamicRouteConfig {
private List<RouteInfo> routeInfos;
}

View File

@ -0,0 +1,53 @@
package cn.com.do1.component.bi.gateway.config.DynamicRoute;
import cn.hutool.core.util.ObjectUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 动态路由转换器
*/
public class DynamicRouteConfigConverter {
private final static Map<String, List<String>> instanceToTenantIds=new HashMap<>();
private final static Map<String, List<String>> tenantIdToInstances=new HashMap<>();
public static Map<String, List<String>> getTenantIdToInstances(){
return tenantIdToInstances;
}
public static Map<String, List<String>> getInstanceToTenantIds(){
return instanceToTenantIds;
}
public static Map<String, List<String>> convertToInstanceToTenantIds(List<RouteInfo> routeInfos){
for (RouteInfo routeInfo:routeInfos){
DynamicRouteConfigConverter.instanceToTenantIds.putAll(routeInfo.getInstanceToTenantIds());
}
return instanceToTenantIds;
}
public static Map<String, List<String>> convertToTenantIdToInstances(List<RouteInfo> routeInfos){
routeInfos.stream().forEach(routeInfo -> {
if (ObjectUtil.isNotEmpty(routeInfo.getInstanceToTenantIds())){
routeInfo.getInstanceToTenantIds().entrySet().stream().forEach(entry -> entry.getValue().stream().forEach(tenantId -> {
String instanceTenantIdKey = routeInfo.getId() + tenantId;
List<String> instances = tenantIdToInstances.getOrDefault(instanceTenantIdKey, new ArrayList<String>());
instances.add( entry.getKey());
tenantIdToInstances.put(instanceTenantIdKey,instances);
}));
}
});
return tenantIdToInstances;
}
}

View File

@ -0,0 +1,95 @@
package cn.com.do1.component.bi.gateway.config.DynamicRoute;
import cn.com.do1.component.bi.gateway.filter.ReqDecryptFilter;
import cn.com.do1.component.bi.gateway.filter.RespEncryptFilter;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.cloud.gateway.route.builder.UriSpec;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpCookie;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.function.Function;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
@Configuration
public class GatewayConfig {
@Autowired
private DynamicRouteConfig dynamicRouteConfig;
@Bean
public RouteLocator headerRoutes(RouteLocatorBuilder builder){
RouteLocatorBuilder.Builder routes = builder.routes();
List<RouteInfo> routeInfos = dynamicRouteConfig.getRouteInfos();
if (ObjectUtil.isNotEmpty(routeInfos)){
DynamicRouteConfigConverter.convertToTenantIdToInstances(routeInfos);
for (RouteInfo routeInfo:routeInfos){
routes.route(routeInfo.getId(), r ->{
return r.path(routeInfo.getPredicatesPaths().get(0))
.filters(new Function<GatewayFilterSpec, UriSpec>() {
@Override
public UriSpec apply(GatewayFilterSpec gatewayFilterSpec) {
Map<String, String> rewritePathMap = routeInfo.getRewritePathMap();
if (ObjectUtil.isNotEmpty(rewritePathMap)) {
gatewayFilterSpec.rewritePath(rewritePathMap.get("regexp"),rewritePathMap.get("replacement"));
}
return gatewayFilterSpec.filter(new HeaderToPathGatewayFilter(routeInfo.getId()))
.filter(new ReqDecryptFilter(-25))
.filter(new RespEncryptFilter(-23));
}
}).uri(routeInfo.getDefaultUri());
});
}
}
return routes.build();
}
/**
* 租户路由过滤器
*/
public class HeaderToPathGatewayFilter implements GatewayFilter{
private String routeInfoId;
public HeaderToPathGatewayFilter(String routeInfoId) {
this.routeInfoId = routeInfoId;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
List<HttpCookie> tenantIdCookies = exchange.getRequest().getCookies().get("tenantId");
if (ObjectUtil.isNotEmpty(tenantIdCookies)){
String tenantId = tenantIdCookies.get(0).getValue();
if (ObjectUtil.isEmpty(tenantId)){
Map<String, List<String>> tenantIdToInstances = DynamicRouteConfigConverter.getTenantIdToInstances();
String instanceTenantIdKey =routeInfoId + tenantId;
if (tenantIdToInstances.containsKey(instanceTenantIdKey)) {
List<String> targetUris = tenantIdToInstances.get(instanceTenantIdKey);
int asInt = new Random().ints(0, targetUris.size()).findFirst().getAsInt();
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
Route newRoute = Route.async().asyncPredicate(route.getPredicate()).filters(route.getFilters()).id(route.getId())
.order(route.getOrder()).uri(targetUris.get(asInt)).build();
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR,newRoute);
}
}
}
return chain.filter(exchange);
}
}
}

View File

@ -0,0 +1,108 @@
package cn.com.do1.component.bi.gateway.config.DynamicRoute;
import com.alibaba.fastjson.JSON;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 路由信息配置
*/
public class RouteInfo {
/**
* 路由规则id
*/
private String id;
/**
* 服务实例对应租户id
*/
private Map<String, List<String>> instanceToTenantIds;
/**
* 默认路由规则
*/
private String defaultUri;
/**
* 断言请求路径
*/
private List<String> predicatesPaths;
/**
* 过滤器重写路径匹配
*/
private Map<String,String> rewritePathMap;
private Map<String,String> filters;
public Map<String, String> getRewritePathMap() {
return rewritePathMap;
}
public void setRewritePathMap(Map<String, String> rewritePathMap) {
this.rewritePathMap = rewritePathMap;
}
public List<String> getPredicatesPaths() {
return predicatesPaths;
}
public void setPredicatesPaths(List<String> predicatesPaths) {
this.predicatesPaths = predicatesPaths;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Map<String, List<String>> getInstanceToTenantIds() {
return instanceToTenantIds;
}
public void setInstanceToTenantIds(Map<String, List<String>> instanceToTenantIds) {
this.instanceToTenantIds = instanceToTenantIds;
}
public String getDefaultUri() {
return defaultUri;
}
public void setDefaultUri(String defaultUri) {
this.defaultUri = defaultUri;
}
public static void main(String[] args) {
RouteInfo routeInfo = new RouteInfo();
routeInfo.setId("bi-system");
List<String> strings = new ArrayList<>();
strings.add("1");
strings.add("3");
strings.add("4");
List<String> strings1 = new ArrayList<>();
strings1.add("2");
List<String> predicatesPaths = new ArrayList<>();
predicatesPaths.add("/system/**");
Map<String, List<String>> stringListHashMap = new HashMap<>();
stringListHashMap.put("lb://bi-system-1",strings);
stringListHashMap.put("lb://bi-system-2",strings1);
routeInfo.setInstanceToTenantIds(stringListHashMap);
routeInfo.setDefaultUri("lb://bi-system");
routeInfo.setPredicatesPaths(predicatesPaths);
Map<String, String> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("'/' + serviceId.replace('bi-', '') + '/(?<remaining>.*)'","'/${remaining}'");
routeInfo.setRewritePathMap(stringStringHashMap);
List<RouteInfo> routeInfos = new ArrayList<>();
routeInfos.add(routeInfo);
String s = JSON.toJSONString(routeInfos);
List<RouteInfo> routeInfos1 = JSON.parseArray(s, RouteInfo.class);
System.out.println(s);
}
}

View File

@ -0,0 +1,68 @@
package cn.com.do1.component.bi.gateway.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 网关的swagger的配置类
*/
@Component
@Primary
public class GatewaySwaggerResourcesProvider implements SwaggerResourcesProvider {
/**
* swagger3默认的url后缀
*/
private static final String SWAGGER2URL = "/v3/api-docs";
/**
* 网关路由
*/
@Autowired
private RouteLocator routeLocator;
@Autowired
private GatewayProperties gatewayProperties;
/**
* 网关应用名称
*/
@Value("${spring.application.name}")
private String self;
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routeHosts = new ArrayList<>();
// 获取所有可用的hostserviceId
routeLocator.getRoutes().filter(route -> route.getUri().getHost() != null)
.filter(route -> !self.equals(route.getUri().getHost()))
.subscribe(route -> routeHosts.add(route.getUri().getHost()));
// 记录已经添加过的server
Set<String> dealed = new HashSet<>();
routeHosts.forEach(instance -> {
// 拼接url
String url = "/" + instance.toLowerCase().split("-")[1] + SWAGGER2URL;
if (!dealed.contains(url)) {
dealed.add(url);
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setUrl(url);
swaggerResource.setName(instance);
resources.add(swaggerResource);
}
});
return resources;
}
}

View File

@ -0,0 +1,51 @@
package cn.com.do1.component.bi.gateway.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Configuration
@RefreshScope
@Data
@ConfigurationProperties(prefix = "encrypt-decrypt")
public class ReqDecryptAndRespEncryptConfig {
/**
* 开启req的body解密true开启
*/
private Boolean reqDecrypt = false;
/**
* 是否开启Response加密true开启
*/
private Boolean respEncrypt = false;
/**
* 加解密白名单白名单内url不处理
*/
private List<String> whiteUrl = new ArrayList<>();
/**
* rsa加密公钥
*/
private String rsaPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9Z3S1r2wPogXFY2UH2vzBgzUEdvXDq6j0DHnJ5YU5pF" +
"Ipgnz+G1Uf+5a5VzDcANrCT/GVqZAKPwbQM776eDv/xy1i4sS7wsI9m6pKQ3HSAJl1knT36qfKthMd6VdOQLAZuf9pH3ccbpO3GiJNWAfee/nJ" +
"egPK0Xle4Fl114vaiQIDAQAB";
/**
* rsa加密私钥
*/
private String rsaPrivateKey = "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAL1ndLWvbA+iBcVj" +
"ZQfa/MGDNQR29cOrqPQMecnlhTmkUimCfP4bVR/7lrlXMNwA2sJP8ZWpkAo/BtAz" +
"vvp4O//HLWLixLvCwj2bqkpDcdIAmXWSdPfqp8q2Ex3pV05AsBm5/2kfdxxuk7ca" +
"Ik1YB957+cl6A8rReV7gWXXXi9qJAgMBAAECgYBJpwp2gHHoHlxaJs2p4Vl6stgS" +
"FWR6o60+wf82KL/G64RbyfdrJRvUJRS2nBZO5zIqb8YFKfvuUBYJLqYsZkcGA58H" +
"rxIt18bwdtbF3NEgPNLfnMhisD4KcMeBWySe+OlITiFY2IVMtCHAGT/lhSphkTru" +
"UoRc4VfmFRLBy3g/hQJBAN0HeuFQS2zXeAItc6E7YoInTQb01Fm4Ax1pmViQQCke" +
"Z6+Qe15jvktlppYJ0DM96lSPpDcBDJeMqM9vu5anBo8CQQDbXwo1VEkE+cROU+Cc" +
"0YqvxDFJonp9iDza2ZzO1ilo0lLZWKYdG41gFDdSACwCNFq39X769c+eCV2b5iH6" +
"J9lnAkEAvrrarZ1lSMnydCaWljYxflC9plgU+krQ3UunmQX5Z8ImBRjvbHcz2cog" +
"424abG1sTYYaVaChJhGqBj7LqGf/PwJBAIlo18Ed4XsvZEpX+drg2klM0D66epWF" +
"L/E53CInPdr9241vHOYgqwaiwyAnIWnkF2shaH+UV487eJo9pczHB0MCQQCwqgF6" +
"rs6IyoFb/acqVm0mH5o6ROPthW5CobjoO7R3YcKQ1Mr5iHZcYKOPTnibLOlclEsH" +
"CrPUk5sdCSkjd9NP";
}

View File

@ -0,0 +1,31 @@
package cn.com.do1.component.bi.gateway.ctl;
import cn.com.do1.component.bi.gateway.config.ReqDecryptAndRespEncryptConfig;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/env")
public class GatewayController {
@Autowired
private ReqDecryptAndRespEncryptConfig reqDecryptAndRespEncryptConfig;
@GetMapping("secret")
public ResponseEntity<String> secret() {
JSONObject json = new JSONObject();
json.put("code",0);
JSONObject sonJson = new JSONObject();
if(reqDecryptAndRespEncryptConfig.getReqDecrypt() || reqDecryptAndRespEncryptConfig.getRespEncrypt()){
sonJson.put("reqDecrypt",reqDecryptAndRespEncryptConfig.getReqDecrypt());
sonJson.put("respEncrypt",reqDecryptAndRespEncryptConfig.getRespEncrypt());
sonJson.put("publicKey", reqDecryptAndRespEncryptConfig.getRsaPublicKey());
sonJson.put("privateKey", reqDecryptAndRespEncryptConfig.getRsaPrivateKey());
}
json.put("data",sonJson);
return ResponseEntity.ok(json.toString());
}
}

View File

@ -0,0 +1,136 @@
package cn.com.do1.component.bi.gateway.filter;
import cn.com.do1.component.bi.gateway.config.ReqDecryptAndRespEncryptConfig;
import cn.com.do1.component.bi.gateway.util.SpringContextUtils;
import cn.com.do1.component.bi.gateway.util.encrypt.AESEncryptUtil;
import cn.com.do1.component.bi.gateway.util.encrypt.RSAEncryptUtil;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@DependsOn("reqDecryptAndRespEncryptConfig")
public class ReqDecryptFilter implements GatewayFilter, Ordered {
private static final Logger logger = LoggerFactory.getLogger(ReqDecryptFilter.class);
private int order;
private static ReqDecryptAndRespEncryptConfig reqDecryptAndRespEncryptConfig = null;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if(reqDecryptAndRespEncryptConfig == null){
reqDecryptAndRespEncryptConfig = SpringContextUtils.getBean("reqDecryptAndRespEncryptConfig");
}
ServerHttpRequest request = exchange.getRequest();
if(!reqDecryptAndRespEncryptConfig.getReqDecrypt()){
//关闭解密
return chain.filter(exchange);
}
if(reqDecryptAndRespEncryptConfig.getWhiteUrl().size()>0){
for(String url : reqDecryptAndRespEncryptConfig.getWhiteUrl()){
if(request.getURI().getPath().startsWith(url)){
//白名单url直接跳过
return chain.filter(exchange);
}
}
}
HttpMethod requestMethod = request.getMethod();
if (!HttpMethod.POST.equals(requestMethod) && !HttpMethod.PUT.equals(requestMethod)) {
return chain.filter(exchange);
}
// 获取请求的Content-Type
String contentType = request.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
if (!MediaType.APPLICATION_JSON_VALUE.equals(contentType)) {
return chain.filter(exchange);
}
return readBody(exchange, chain);
}
ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage) {
return new ServerHttpRequestDecorator(exchange.getRequest()) {
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0L) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set("Transfer-Encoding", "chunked");
}
return httpHeaders;
}
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
}
private Mono<Void> returnMononew(GatewayFilterChain chain,ServerWebExchange exchange){
return chain.filter(exchange).then(Mono.fromRunnable(()->{
}));
}
private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {
//重新构造request参考ModifyRequestBodyGatewayFilterFactory
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
//重点
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
String newBody = decryptAES(body);
return Mono.just(newBody);
});
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
//猜测这个就是之前报400错误的元凶之前修改了body但是没有重新写content length
headers.remove("Content-Length");
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
ServerHttpRequest decorator = this.decorate(exchange, headers, outputMessage);
return returnMononew(chain, exchange.mutate().request(decorator).build());
}));
}
private String decryptAES(String encryptedText) {
// 解密逻辑
JSONObject param = JSONObject.parseObject(encryptedText);
if(param.containsKey("encrypted") && param.containsKey("requestData")){
try {
String clientKey = RSAEncryptUtil.decryptByPrivateKey(param.getString("encrypted"),reqDecryptAndRespEncryptConfig.getRsaPrivateKey());
return AESEncryptUtil.decrypt(param.getString("requestData"), clientKey);
} catch (Exception e) {
logger.error("解密接口出现问题",e);
}
}else{
logger.debug("需要解密内容为空");
}
return encryptedText;
}
@Override
public int getOrder() {
return this.order;
}
public ReqDecryptFilter(int order){
this.order = order;
}
}

View File

@ -0,0 +1,116 @@
package cn.com.do1.component.bi.gateway.filter;
import cn.com.do1.component.bi.gateway.config.ReqDecryptAndRespEncryptConfig;
import cn.com.do1.component.bi.gateway.util.RandomStringUtils;
import cn.com.do1.component.bi.gateway.util.SpringContextUtils;
import cn.com.do1.component.bi.gateway.util.encrypt.AESEncryptUtil;
import cn.com.do1.component.bi.gateway.util.encrypt.RSAEncryptUtil;
import com.alibaba.fastjson.JSONObject;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
public class RespEncryptFilter implements GatewayFilter, Ordered {
private static final Logger logger = LoggerFactory.getLogger(RespEncryptFilter.class);
private int order;
private static ReqDecryptAndRespEncryptConfig reqDecryptAndRespEncryptConfig = null;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if(reqDecryptAndRespEncryptConfig == null){
reqDecryptAndRespEncryptConfig = SpringContextUtils.getBean("reqDecryptAndRespEncryptConfig");
}
if(!reqDecryptAndRespEncryptConfig.getRespEncrypt()){
//关闭解密
return chain.filter(exchange);
}
if(reqDecryptAndRespEncryptConfig.getWhiteUrl().size()>0){
for(String url : reqDecryptAndRespEncryptConfig.getWhiteUrl()){
if(request.getURI().getPath().startsWith(url)){
//白名单url直接跳过
return chain.filter(exchange);
}
}
}
ServerHttpResponse originalResponse = exchange.getResponse();
originalResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON);
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (getStatusCode().equals(HttpStatus.OK) && body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffer);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
//释放掉内存
DataBufferUtils.release(join);
// 正常返回的数据
String rootData = new String(content, StandardCharsets.UTF_8);
logger.debug("--------"+request.getURI().getPath()+";"+rootData);
JSONObject result = JSONObject.parseObject(rootData);
if(!result.containsKey("code")){
return bufferFactory.wrap(rootData.getBytes());
}
JSONObject json = new JSONObject();
json.put("code", result.getInteger("code"));
json.put("message", result.getString("message"));
json.put("traceId", result.getString("traceId"));
try {
String data = result.getString("data");
if(result.containsKey("code") && 0 == result.getInteger("code") && StringUtils.hasLength(data)){
String aesKey = RandomStringUtils.generateRandomString(16);
String key = RSAEncryptUtil.encryptByPublicKeyToBase64(aesKey.getBytes(),reqDecryptAndRespEncryptConfig.getRsaPublicKey());
String encryptData = AESEncryptUtil.encrypt(data,aesKey);
JSONObject sonJson = new JSONObject();
sonJson.put("encrypted", key);
sonJson.put("responseData", encryptData);
json.put("data",sonJson);
}else{
json.put("data", null);
}
} catch (Exception e) {
logger.error("加密response失败",e);
}
// 加密后的数据返回给客户端
byte[] uppedContent = json.toJSONString().getBytes(StandardCharsets.UTF_8);
originalResponse.getHeaders().setContentLength(uppedContent.length);
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return this.order;
}
public RespEncryptFilter(int order){
this.order = order;
}
}

View File

@ -0,0 +1,18 @@
package cn.com.do1.component.bi.gateway.util;
import java.util.Random;
public class RandomStringUtils {
private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
public static String generateRandomString(int length) {
StringBuilder sb = new StringBuilder(length);
Random random = new Random();
for (int i = 0; i < length; i++) {
int index = random.nextInt(CHARACTERS.length());
sb.append(CHARACTERS.charAt(index));
}
return sb.toString();
}
}

View File

@ -0,0 +1,31 @@
package cn.com.do1.component.bi.gateway.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
public SpringContextUtils() {
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static ApplicationContext getContext() {
return applicationContext;
}
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}

View File

@ -0,0 +1,73 @@
package cn.com.do1.component.bi.gateway.util.encrypt;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.util.Base64;
@Slf4j
public class AESEncryptUtil {
public AESEncryptUtil() {
}
/**
* 加密
* @param input
* @param key
* @return
*/
public static String encrypt(String input, String key) {
String result = null;
try {
byte[] crypted = null;
SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skey);
crypted = cipher.doFinal(input.getBytes("UTF-8"));
result = encryptBASE64(crypted);
} catch (Exception e) {
System.out.println(e.toString());
}
return result;
}
public static void main(String[] args) throws Exception {
String key = "niaze7pp0zho1mgp";
String str = encrypt("{\"type\":2,\"pageSize\":50,\"currPage\":1}",key);
System.out.println(str);
System.out.println(decrypt(str,key));
}
/**
* 解密
* @param input
* @param key
* @return
*/
public static String decrypt(String input, String key) {
String result = null;
try {
byte[] output = null;
SecretKeySpec skey = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skey);
output = cipher.doFinal(decryptBASE64(input));
result = new String(output, "UTF-8");
} catch (Exception e) {
log.info(e.toString());
}
return result;
}
public static byte[] decryptBASE64(String key) throws IOException {
return Base64.getDecoder().decode(key);
}
public static String encryptBASE64(byte[] key) {
return Base64.getEncoder().encodeToString(key);
}
}

View File

@ -0,0 +1,144 @@
package cn.com.do1.component.bi.gateway.util.encrypt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class RSAEncryptUtil {
private static final Logger logger = LoggerFactory.getLogger(RSAEncryptUtil.class);
public RSAEncryptUtil() {
}
public static String encryptBASE64(byte[] key) {
return Base64.getEncoder().encodeToString(key);
}
public static byte[] decryptBASE64(String key) {
return Base64.getDecoder().decode(key);
}
public static Map<String, Object> initKey() throws Exception {
Map<String, Object> keyMap = new HashMap(2);
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(1024);
KeyPair keyPair = keyPairGen.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey)keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey)keyPair.getPrivate();
keyMap.put("RSAPublicKey", publicKey);
keyMap.put("RSAPrivateKey", privateKey);
return keyMap;
}
public static String getPrivateKey(Map<String, Object> keyMap) {
Key key = (Key)keyMap.get("RSAPrivateKey");
return encryptBASE64(key.getEncoded());
}
public static String getPublicKey(Map<String, Object> keyMap) {
Key key = (Key)keyMap.get("RSAPublicKey");
return encryptBASE64(key.getEncoded());
}
public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(2, publicKey);
return cipher.doFinal(data);
}
public static String decryptByPrivateKey(String data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(2, privateKey);
//当长度过长的时候需要分割后解密 128个字节
return new String(getMaxResultDecrypt(data, cipher));
}
private static byte[] getMaxResultDecrypt(String data, Cipher cipher) throws IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {
byte[] inputArray = decryptBASE64(data);
int inputLength = inputArray.length;
// 最大解密字节数超出最大字节数需要分组加密
int MAX_ENCRYPT_BLOCK = 128;
// 标识
int offSet = 0;
byte[] resultBytes = {};
byte[] cache = {};
while (inputLength - offSet > 0) {
if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
offSet += MAX_ENCRYPT_BLOCK;
} else {
cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
offSet = inputLength;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
}
return resultBytes;
}
public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(1, publicKey);
return cipher.doFinal(data);
}
public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(1, privateKey);
return cipher.doFinal(data);
}
public static void main(String[] args) throws Exception {
Map<String, Object> keyMap = initKey();
String publicKey = getPublicKey(keyMap);
String privateKey = getPrivateKey(keyMap);
System.out.println("公钥: \n\r" + publicKey);
String str = encryptByPublicKeyToBase64("crkXipxtMn5I4KXwwLMkeA==".getBytes(),publicKey);
System.out.println("公钥加密后: \n\r" + str);
System.out.println("私钥: \n\r" + privateKey);
//System.out.println("私钥解密后: \n\r" + decryptByPrivateKey(str.getBytes(),privateKey));
}
public static String encryptByPublicKeyToBase64(byte[] data, String key) throws Exception {
byte[] keyBytes = decryptBASE64(key);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
Key publicKey = keyFactory.generatePublic(x509KeySpec);
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(1, publicKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(data));
}
}

View File

@ -0,0 +1,32 @@
server:
port: 80
spring:
application:
name: bi-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.83.10:32610
namespace: bi-system
config:
server-addr: 192.168.83.10:32610
file-extension: yml
namespace: bi-system
group: DEFAULT_GROUP
# 配置组,优先级从上至下递增,最下方的优先级最高
shared-configs:
- data-id: global.yml
- data-id: bi-gateway.yml
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+ serviceId.replace('bi-', '') +'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId.replace('bi-', '') + '/(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"

View File

@ -0,0 +1,42 @@
server:
port: 80
spring:
application:
name: saas-cpn-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: properties
namespace: public
group: DEFAULT_GROUP
extension-configs[0]:
data-id: redis.properties
group: DEFAULT_GROUP
refresh: true
extension-configs[1]:
data-id: global.properties
group: DEFAULT_GROUP
refresh: true
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+serviceId+'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId + '/?(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"
routes:
- id: default_to_all
uri: lb://saas-dubbo-all-application
order: 10
predicates:
- Path=/**
filters:
- RewritePath=/saas-dubbo-[-\w]+/, /

View File

@ -0,0 +1,32 @@
server:
port: 80
spring:
application:
name: bi-gateway
# profiles:
# active: local #启动环境 dev开发环境 local本地环境 prodprod环境
cloud:
nacos:
discovery:
server-addr: 192.168.83.10:32610
namespace: bi-system-local
config:
server-addr: 192.168.83.10:32610
file-extension: yml
namespace: bi-system-local
group: DEFAULT_GROUP
# 配置组,优先级从上至下递增,最下方的优先级最高
shared-configs:
- data-id: bi-gateway.yml
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+ serviceId.replace('bi-', '') +'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId.replace('bi-', '') + '/(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"

View File

@ -0,0 +1,27 @@
server:
port: 80
spring:
application:
name: bi-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.83.10:32610
namespace: bi-system
config:
server-addr: 192.168.83.10:32610
file-extension: yml
namespace: bi-system
group: DEFAULT_GROUP
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+ serviceId.replace('bi-', '') +'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId.replace('bi-', '') + '/(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"

View File

@ -0,0 +1,49 @@
spring:
cloud:
sentinel:
enabled: false # sentinel开关
eager: false
transport:
port: 8089
dashboard: http://127.0.0.1:8858 # Sentinel控制台地址
datasource:
# 流控规则
flow:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-flow-rules
namespace: sentinel
file-extension: json
rule-type: flow
# 降级规则
degrade:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-degrade-rules
namespace: sentinel
file-extension: json
rule-type: degrade
# 热点规则
param-flow:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-param-flow-rules
namespace: sentinel
file-extension: json
rule-type: param-flow
# 系统规则
system:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-system-rules
namespace: sentinel
file-extension: json
rule-type: system
# 授权规则
authority:
nacos:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
dataId: ${spring.application.name}-authority-rules
namespace: sentinel
file-extension: json
rule-type: authority

View File

@ -0,0 +1,27 @@
server:
port: 80
spring:
application:
name: bi-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.83.10:32610
namespace: bi-system
config:
server-addr: 192.168.83.10:32610
file-extension: yml
namespace: bi-system
group: DEFAULT_GROUP
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+ serviceId.replace('bi-', '') +'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId.replace('bi-', '') + '/(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"

View File

@ -0,0 +1,43 @@
server:
port: 80
spring:
application:
name: saas-cpn-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.83.219:8848
namespace: ybg
config:
server-addr: 192.168.83.219:8848
file-extension: properties
namespace: ybg
group: DEFAULT_GROUP
extension-configs[0]:
data-id: redis.properties
group: DEFAULT_GROUP
refresh: true
extension-configs[1]:
data-id: global.properties
group: DEFAULT_GROUP
refresh: true
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+serviceId+'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId + '/?(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"
routes:
- id: default_to_all
uri: lb://wxqyh
order: 10
predicates:
- Path=/**
filters:
- RewritePath=/saas-dubbo-[-\w]+/, /

View File

@ -0,0 +1,34 @@
server:
port: 80
spring:
application:
name: bi-gateway
profiles:
active: local #启动环境 dev开发环境 local本地环境 prodprod环境
include: sentinel
cloud:
nacos:
discovery:
server-addr: 192.168.83.10:32610
namespace: bi-system
config:
server-addr: 192.168.83.10:32610
file-extension: yml
namespace: bi-system
group: DEFAULT_GROUP
# 配置组,优先级从上至下递增,最下方的优先级最高
shared-configs:
- data-id: global.yml
- data-id: bi-gateway.yml
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true #小写的服务名称
predicates[0]:
name: Path
args[pattern]: "'/'+ serviceId.replace('bi-', '') +'/**'"
filters[0]:
name: CustomRewritePath
args[regexp]: "'/' + serviceId.replace('bi-', '') + '/(?<remaining>.*)'"
args[replacement]: "'/${remaining}'"

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<springProperty scope="context" name="application" source="spring.application.name"/>
<springProperty scope="context" name="profile" source="spring.profiles.active"/>
<springProperty scope="context" name="logStashEnable" source="logging.logstash.enable" defaultValue="false"/>
<springProperty scope="context" name="logStashHost" source="logging.logstash.host"/>
<springProperty scope="context" name="logStashPort" source="logging.logstash.port"/>
<springProperty scope="context" name="LOG_HOME" source="logging.file" defaultValue="./logs"/>
<property name="LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%level %thread] [%X{traceId}-%X{spanId}] %logger{50} - %msg %n" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/${application}.log.%d{yyyy-MM-dd}.%i</FileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<!-- or whenever the file size reaches 10MB -->
<maxFileSize>1024MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符-->
<pattern>${LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<if condition='property("logStashEnable").contains("true")'>
<then>
<!-- Appender to log to file in a JSON format -->
<appender name="logStash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>${logStashHost}:${logStashPort}</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
</then>
</if>
<!-- 开发、测试环境 -->
<springProfile name="dev,test,local,poc,qa">
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.springboot.sample" level="INFO" />
<logger name="cn.com.do1.component" level="INFO"/>
<logger name="com.alibaba.nacos.client.naming" level="ERROR" />
<logger name="cn.com.do1.component" level="DEBUG"/>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<logger name="org.springframework.web" level="ERROR"/>
<logger name="org.springboot.sample" level="ERROR" />
<logger name="com.alibaba.nacos.client.naming" level="ERROR" />
<logger name="cn.com.do1.component" level="ERROR" />
</springProfile>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
<if condition='property("logStashEnable").contains("true")'>
<then>
<appender-ref ref="logStash"/>
</then>
</if>
</root>
</configuration>