{
+
+ static AppRateLimitHandler create(String instance) {
+ switch (instance) {
+ case "redis":
+ default:
+ // 本地缓存
+ return new AppRateLimitHandlerImpl();
+ }
+
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java
new file mode 100644
index 0000000..4487e53
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/AppRateLimitHandlerImpl.java
@@ -0,0 +1,48 @@
+/*
+package com.sf.vertx.handle;
+
+import com.sf.vertx.constans.RedisKeyConfig;
+import com.sf.vertx.enums.GatewayError;
+import com.sf.vertx.exception.ServiceException;
+import com.sf.vertx.pojo.SacCurrentLimiting;
+
+import io.github.resilience4j.core.functions.CheckedRunnable;
+import io.github.resilience4j.ratelimiter.RateLimiter;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import lombok.extern.slf4j.Slf4j;
+
+*/
+/***
+ * 内存存储
+ *
+ * @author xy
+ *
+ *//*
+
+@Slf4j
+public class AppRateLimitHandlerImpl implements AppRateLimitHandler {
+
+ @Override
+ public void handle(RoutingContext rc) {
+ log.info("Enter AppRateLimitHandlerImpl.handle()");
+ String appCode = rc.request().headers().get(AppConfigHandler.getAppCodeHeaderKey());
+ SacCurrentLimiting currentLimiting = AppConfigHandler.getGlobalAppCurrentLimitingConfig(appCode);
+
+ if (currentLimiting != null) {
+ String key = RedisKeyConfig.APP_CURRENT_LIMITING_CONFIG_KEY + ":" + appCode;
+ RateLimiter rateLimiter = currentLimiting.getRegistry().rateLimiter(key);
+ CheckedRunnable restrictedCall = RateLimiter.decorateCheckedRunnable(rateLimiter, rc::next);
+ try {
+ restrictedCall.run();
+ } catch (Throwable t) {
+ //t.printStackTrace();
+ log.info("app ratelimit:{}", key);
+ rc.fail(new ServiceException(GatewayError.REQUEST_URL_RESTRICTED_BY_FLOW, currentLimiting.getStrategy().getDefaultResponse()));
+ }
+ } else {
+ rc.next();
+ }
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java
new file mode 100644
index 0000000..34b1d5b
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandler.java
@@ -0,0 +1,178 @@
+/*
+package com.sf.vertx.handle;
+
+*/
+/*
+ * Copyright 2014 Red Hat, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * The Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php
+ *
+ * You may elect to redistribute this code under either of these licenses.
+ *//*
+
+
+import io.vertx.codegen.annotations.Fluent;
+import io.vertx.codegen.annotations.VertxGen;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+
+*/
+/**
+ * A handler which gathers the entire request body and sets it on the {@link RoutingContext}.
+ *
+ * It also handles HTTP file uploads and can be used to limit body sizes.
+ *
+ * @author Tim Fox
+ *//*
+
+@VertxGen
+public interface BodyHandler extends Handler {
+
+ */
+/**
+ * Default max size for a request body in bytes = {@code 10485760}, i.e. 10 megabytes
+ *//*
+
+ long DEFAULT_BODY_LIMIT = 10 * 1024 * 1024;
+
+ */
+/**
+ * Default uploads directory on server for file uploads
+ *//*
+
+ String DEFAULT_UPLOADS_DIRECTORY = "file-uploads";
+
+ */
+/**
+ * Default value of whether form attributes should be merged into request params
+ *//*
+
+ boolean DEFAULT_MERGE_FORM_ATTRIBUTES = true;
+
+ */
+/**
+ * Default value of whether uploaded files should be removed after handling the request
+ *//*
+
+ boolean DEFAULT_DELETE_UPLOADED_FILES_ON_END = false;
+
+ */
+/**
+ * Default value of whether to pre-allocate the body buffer size according to the content-length HTTP request header
+ *//*
+
+ boolean DEFAULT_PREALLOCATE_BODY_BUFFER = false;
+
+ */
+/**
+ * Create a body handler with defaults.
+ *
+ * @return the body handler
+ *//*
+
+ static BodyHandler create() {
+ return new BodyHandlerImpl();
+ }
+
+ */
+/**
+ * Create a body handler setting if it should handle file uploads.
+ *
+ * @param handleFileUploads true if files upload should be handled
+ * @return the body handler
+ *//*
+
+ static BodyHandler create(boolean handleFileUploads) {
+ return new BodyHandlerImpl(handleFileUploads);
+ }
+
+ */
+/**
+ * Create a body handler and use the given upload directory.
+ *
+ * @param uploadDirectory the uploads directory
+ * @return the body handler
+ *//*
+
+ static BodyHandler create(String uploadDirectory) {
+ return new BodyHandlerImpl(uploadDirectory);
+ }
+
+ */
+/**
+ * Set whether file uploads will be handled.
+ *
+ * @param handleFileUploads true if they should be handled
+ * @return reference to this for fluency
+ *//*
+
+ @Fluent
+ BodyHandler setHandleFileUploads(boolean handleFileUploads);
+
+ */
+/**
+ * Set the maximum body size in bytes, {@code -1} means no limit.
+ *
+ * @param bodyLimit the max size in bytes
+ * @return reference to this for fluency
+ *//*
+
+ @Fluent
+ BodyHandler setBodyLimit(long bodyLimit);
+
+ */
+/**
+ * Set the uploads directory to use.
+ *
+ * @param uploadsDirectory the uploads directory
+ * @return reference to this for fluency
+ *//*
+
+ @Fluent
+ BodyHandler setUploadsDirectory(String uploadsDirectory);
+
+ */
+/**
+ * Set whether form attributes will be added to the request parameters.
+ *
+ * @param mergeFormAttributes true if they should be merged
+ * @return reference to this for fluency
+ *//*
+
+ @Fluent
+ BodyHandler setMergeFormAttributes(boolean mergeFormAttributes);
+
+ */
+/**
+ * Set whether uploaded files should be removed after handling the request.
+ *
+ * @param deleteUploadedFilesOnEnd true if uploaded files should be removed after handling the request
+ * @return reference to this for fluency
+ *//*
+
+ @Fluent
+ BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd);
+
+ */
+/**
+ * Pre-allocate the body buffer according to the value parsed from content-length header.
+ * The buffer is capped at 64KB
+ * @param isPreallocateBodyBuffer {@code true} if body buffer is pre-allocated according to the size
+ * read from content-length Header.
+ * {code false} if body buffer is pre-allocated to 1KB, and is resized dynamically
+ * @return reference to this for fluency
+ *//*
+
+ @Fluent
+ BodyHandler setPreallocateBodyBuffer(boolean isPreallocateBodyBuffer);
+
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java
new file mode 100644
index 0000000..bd0becd
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/BodyHandlerImpl.java
@@ -0,0 +1,389 @@
+/*
+package com.sf.vertx.handle;
+
+import java.io.File;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+
+*/
+/*
+ * Copyright 2014 Red Hat, Inc.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Apache License v2.0 which accompanies this distribution.
+ *
+ * The Eclipse Public License is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * The Apache License v2.0 is available at
+ * http://www.opensource.org/licenses/apache2.0.php
+ *
+ * You may elect to redistribute this code under either of these licenses.
+ *//*
+
+
+import com.sf.vertx.api.pojo.ApiConfig;
+import com.sf.vertx.api.pojo.AppConfig;
+import com.sf.vertx.enums.RequestMethod;
+import com.sf.vertx.utils.AppUtils;
+import io.netty.handler.codec.DecoderException;
+import io.netty.handler.codec.http.HttpHeaderValues;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.file.FileSystem;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.http.HttpVersion;
+import io.vertx.core.impl.logging.Logger;
+import io.vertx.core.impl.logging.LoggerFactory;
+import io.vertx.ext.web.FileUpload;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.impl.FileUploadImpl;
+import io.vertx.ext.web.impl.RoutingContextInternal;
+
+import static com.sf.vertx.constans.SACConstants.*;
+
+*/
+/**
+ * @author Tim Fox
+ *//*
+
+public class BodyHandlerImpl implements BodyHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(BodyHandlerImpl.class);
+
+ private long bodyLimit = DEFAULT_BODY_LIMIT;
+ private boolean handleFileUploads;
+ private String uploadsDir;
+ private boolean mergeFormAttributes = DEFAULT_MERGE_FORM_ATTRIBUTES;
+ private boolean deleteUploadedFilesOnEnd = DEFAULT_DELETE_UPLOADED_FILES_ON_END;
+ private boolean isPreallocateBodyBuffer = DEFAULT_PREALLOCATE_BODY_BUFFER;
+ private static final int DEFAULT_INITIAL_BODY_BUFFER_SIZE = 1024; // bytes
+
+ public BodyHandlerImpl() {
+ this(true, DEFAULT_UPLOADS_DIRECTORY);
+ }
+
+ public BodyHandlerImpl(boolean handleFileUploads) {
+ this(handleFileUploads, DEFAULT_UPLOADS_DIRECTORY);
+ }
+
+ public BodyHandlerImpl(String uploadDirectory) {
+ this(true, uploadDirectory);
+ }
+
+ private BodyHandlerImpl(boolean handleFileUploads, String uploadDirectory) {
+ this.handleFileUploads = handleFileUploads;
+ setUploadsDirectory(uploadDirectory);
+ }
+
+ @Override
+ public void handle(RoutingContext context) {
+ // =======源码流程
+ final HttpServerRequest request = context.request();
+ final HttpServerResponse response = context.response();
+ //
+ // we need to keep state since we can be called again on reroute
+ if (!((RoutingContextInternal) context).seenHandler(RoutingContextInternal.BODY_HANDLER)) {
+ ((RoutingContextInternal) context).visitHandler(RoutingContextInternal.BODY_HANDLER);
+
+ // Check if a request has a request body.
+ // A request with a body __must__ either have `transfer-encoding`
+ // or `content-length` headers set.
+ // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
+ final long parsedContentLength = parseContentLengthHeader(request);
+ // http2 never transmits a `transfer-encoding` as frames are chunks.
+ final boolean hasTransferEncoding = request.version() == HttpVersion.HTTP_2
+ || request.headers().contains(HttpHeaders.TRANSFER_ENCODING);
+
+ if (!hasTransferEncoding && parsedContentLength == -1) {
+ // there is no "body", so we can skip this handler
+ context.next();
+ return;
+ }
+
+ // before parsing the body we can already discard a bad request just by
+ // inspecting the content-length against
+ // the body limit, this will reduce load, on the server by totally skipping
+ // parsing the request body
+ if (bodyLimit != -1 && parsedContentLength != -1) {
+ if (parsedContentLength > bodyLimit) {
+ context.fail(413);
+ return;
+ }
+ }
+
+ // handle expectations
+ // https://httpwg.org/specs/rfc7231.html#header.expect
+ final String expect = request.getHeader(HttpHeaders.EXPECT);
+ if (expect != null) {
+ // requirements validation
+ if (expect.equalsIgnoreCase("100-continue")) {
+ // A server that receives a 100-continue expectation in an HTTP/1.0 request MUST
+ // ignore that expectation.
+ if (request.version() != HttpVersion.HTTP_1_0) {
+ // signal the client to continue
+ response.writeContinue();
+ }
+ } else {
+ // the server cannot meet the expectation, we only know about 100-continue
+ context.fail(417);
+ return;
+ }
+ }
+
+ // TODO 改造了这个地方 在真正解析body之前验证是否需要继续解析
+ AppConfig appConfig = AppUtils.getAppConfigFromRoutingContext(context);
+ ApiConfig apiConfig = AppUtils.getApiConfigFromRoutingContext(context);
+ String apiServiceType = context.get(API_SERVICE_TYPE);
+ if (!AppUtils.isAnalysisBody(appConfig,apiConfig,apiServiceType)){
+ context.next();
+ return;
+ }
+
+ final BHandler handler = new BHandler(context, isPreallocateBodyBuffer ? parsedContentLength : -1);
+ boolean ended = request.isEnded();
+ if (!ended) {
+ request
+ // resume the request (if paused)
+ .handler(handler).endHandler(handler::end).resume();
+ }
+ } else {
+ // on reroute we need to re-merge the form params if that was desired
+ if (mergeFormAttributes && request.isExpectMultipart()) {
+ request.params().addAll(request.formAttributes());
+ }
+ context.next();
+ }
+ }
+
+ @Override
+ public BodyHandler setHandleFileUploads(boolean handleFileUploads) {
+ this.handleFileUploads = handleFileUploads;
+ return this;
+ }
+
+ @Override
+ public BodyHandler setBodyLimit(long bodyLimit) {
+ this.bodyLimit = bodyLimit;
+ return this;
+ }
+
+ @Override
+ public BodyHandler setUploadsDirectory(String uploadsDirectory) {
+ this.uploadsDir = uploadsDirectory;
+ return this;
+ }
+
+ @Override
+ public BodyHandler setMergeFormAttributes(boolean mergeFormAttributes) {
+ this.mergeFormAttributes = mergeFormAttributes;
+ return this;
+ }
+
+ @Override
+ public BodyHandler setDeleteUploadedFilesOnEnd(boolean deleteUploadedFilesOnEnd) {
+ this.deleteUploadedFilesOnEnd = deleteUploadedFilesOnEnd;
+ return this;
+ }
+
+ @Override
+ public BodyHandler setPreallocateBodyBuffer(boolean isPreallocateBodyBuffer) {
+ this.isPreallocateBodyBuffer = isPreallocateBodyBuffer;
+ return this;
+ }
+
+ private long parseContentLengthHeader(HttpServerRequest request) {
+ String contentLength = request.getHeader(HttpHeaders.CONTENT_LENGTH);
+ if (contentLength == null || contentLength.isEmpty()) {
+ return -1;
+ }
+ try {
+ long parsedContentLength = Long.parseLong(contentLength);
+ return parsedContentLength < 0 ? -1 : parsedContentLength;
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ private class BHandler implements Handler {
+ private static final int MAX_PREALLOCATED_BODY_BUFFER_BYTES = 65535;
+
+ final RoutingContext context;
+ final long contentLength;
+ Buffer body;
+ boolean failed;
+ final AtomicInteger uploadCount = new AtomicInteger();
+ boolean ended;
+ long uploadSize = 0L;
+ final boolean isMultipart;
+ final boolean isUrlEncoded;
+
+ public BHandler(RoutingContext context, long contentLength) {
+ this.context = context;
+ this.contentLength = contentLength;
+ // the request clearly states that there should
+ // be a body, so we respect the client and ensure
+ // that the body will not be null
+ if (contentLength != -1) {
+ initBodyBuffer();
+ }
+
+ List fileUploads = context.fileUploads();
+
+ final String contentType = context.request().getHeader(HttpHeaders.CONTENT_TYPE);
+ if (contentType == null) {
+ isMultipart = false;
+ isUrlEncoded = false;
+ } else {
+ final String lowerCaseContentType = contentType.toLowerCase();
+ isMultipart = lowerCaseContentType.startsWith(HttpHeaderValues.MULTIPART_FORM_DATA.toString());
+ isUrlEncoded = lowerCaseContentType
+ .startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString());
+ }
+
+ if (isMultipart || isUrlEncoded) {
+ context.request().setExpectMultipart(true);
+ if (handleFileUploads) {
+ makeUploadDir(context.vertx().fileSystem());
+ }
+ context.request().uploadHandler(upload -> {
+ if (bodyLimit != -1 && upload.isSizeAvailable()) {
+ // we can try to abort even before the upload starts
+ long size = uploadSize + upload.size();
+ if (size > bodyLimit) {
+ failed = true;
+ context.cancelAndCleanupFileUploads();
+ context.fail(413);
+ return;
+ }
+ }
+ if (handleFileUploads) {
+ // we actually upload to a file with a generated filename
+ uploadCount.incrementAndGet();
+ String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath();
+ FileUploadImpl fileUpload = new FileUploadImpl(context.vertx().fileSystem(), uploadedFileName,
+ upload);
+ fileUploads.add(fileUpload);
+ Future fut = upload.streamToFileSystem(uploadedFileName);
+ fut.onComplete(ar -> {
+ if (fut.succeeded()) {
+ uploadEnded();
+ } else {
+ context.cancelAndCleanupFileUploads();
+ context.fail(ar.cause());
+ }
+ });
+ }
+ });
+ }
+
+ context.request().exceptionHandler(t -> {
+ context.cancelAndCleanupFileUploads();
+ int sc = 200;
+ if (t instanceof DecoderException) {
+ // bad request
+ sc = 400;
+ if (t.getCause() != null) {
+ t = t.getCause();
+ }
+ }
+ context.fail(sc, t);
+ });
+ }
+
+ private void initBodyBuffer() {
+ int initialBodyBufferSize;
+ if (contentLength < 0) {
+ initialBodyBufferSize = DEFAULT_INITIAL_BODY_BUFFER_SIZE;
+ } else if (contentLength > MAX_PREALLOCATED_BODY_BUFFER_BYTES) {
+ initialBodyBufferSize = MAX_PREALLOCATED_BODY_BUFFER_BYTES;
+ } else {
+ initialBodyBufferSize = (int) contentLength;
+ }
+
+ if (bodyLimit != -1) {
+ initialBodyBufferSize = (int) Math.min(initialBodyBufferSize, bodyLimit);
+ }
+
+ this.body = Buffer.buffer(initialBodyBufferSize);
+ }
+
+ private void makeUploadDir(FileSystem fileSystem) {
+ if (!fileSystem.existsBlocking(uploadsDir)) {
+ fileSystem.mkdirsBlocking(uploadsDir);
+ }
+ }
+
+ @Override
+ public void handle(Buffer buff) {
+ if (failed) {
+ return;
+ }
+ uploadSize += buff.length();
+ if (bodyLimit != -1 && uploadSize > bodyLimit) {
+ failed = true;
+ context.cancelAndCleanupFileUploads();
+ context.fail(413);
+ } else {
+ // multipart requests will not end up in the request body
+ // url encoded should also not, however jQuery by default
+ // post in urlencoded even if the payload is something else
+ if (!isMultipart */
+/* && !isUrlEncoded *//*
+) {
+ if (body == null) {
+ initBodyBuffer();
+ }
+ body.appendBuffer(buff);
+ }
+ }
+ }
+
+ void uploadEnded() {
+ int count = uploadCount.decrementAndGet();
+ // only if parsing is done and count is 0 then all files have been processed
+ if (ended && count == 0) {
+ doEnd();
+ }
+ }
+
+ void end(Void v) {
+ // this marks the end of body parsing, calling doEnd should
+ // only be possible from this moment onwards
+ ended = true;
+
+ // only if parsing is done and count is 0 then all files have been processed
+ if (uploadCount.get() == 0) {
+ doEnd();
+ }
+ }
+
+ void doEnd() {
+
+ if (failed || context.failed()) {
+ context.cancelAndCleanupFileUploads();
+ return;
+ }
+
+ if (deleteUploadedFilesOnEnd) {
+ context.addBodyEndHandler(x -> context.cancelAndCleanupFileUploads());
+ }
+
+ HttpServerRequest req = context.request();
+ if (mergeFormAttributes && req.isExpectMultipart()) {
+ req.params().addAll(req.formAttributes());
+ }
+ ((RoutingContextInternal) context).setBody(body);
+ // release body as it may take lots of memory
+ body = null;
+
+ context.next();
+ }
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java
new file mode 100644
index 0000000..20f8f31
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ConsulHandler.java
@@ -0,0 +1,111 @@
+/*
+package com.sf.vertx.handle;
+
+import com.alibaba.fastjson2.JSONObject;
+
+import io.vertx.core.Vertx;
+import io.vertx.ext.consul.ConsulClientOptions;
+import io.vertx.ext.consul.KeyValue;
+import io.vertx.ext.consul.KeyValueList;
+import io.vertx.ext.consul.Watch;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ConsulHandler {
+
+ public static void init(Vertx vertx) {
+ ConsulClientOptions options = new ConsulClientOptions().setHost("127.0.0.1").setPort(8500);
+
+ Watch.keyPrefix("apiCode_", vertx, options)
+ .setHandler(res -> {
+ if (res.succeeded()) {
+ //
+ // 删除
+ boolean isDel = (res.nextResult() == null || res.nextResult().getList() == null) && (res.prevResult() != null && res.prevResult().getList() != null);
+// if(isDel) {
+// log.info("isDel");
+// KeyValueList keyValueList = res.prevResult();
+// if(keyValueList != null && keyValueList.getList() != null) {
+// for(KeyValue keyValue : keyValueList.getList()) {
+// log.info("keyValue:{}", JSONObject.toJSONString(keyValue));
+// }
+// }
+// }
+// // 新增
+// boolean isAdd = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() == null || res.prevResult().getList() == null);
+// if(isAdd) {
+// log.info("isAdd");
+// KeyValueList keyValueList = res.nextResult();
+// if(keyValueList != null && keyValueList.getList() != null) {
+// for(KeyValue keyValue : keyValueList.getList()) {
+// log.info("keyValue:{}", JSONObject.toJSONString(keyValue));
+// }
+// }
+// }
+//
+// // 修改
+// boolean isModify = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() != null && res.prevResult().getList() != null);
+// if(isModify) {
+// log.info("isModify");
+// KeyValueList keyValueList = res.nextResult();
+// if(keyValueList != null && keyValueList.getList() != null) {
+// for(KeyValue keyValue : keyValueList.getList()) {
+// log.info("keyValue:{}", JSONObject.toJSONString(keyValue));
+// }
+// }
+// }
+ } else {
+ res.cause().printStackTrace();
+ }
+ })
+ .start();
+ }
+
+ public static void init1(Vertx vertx) {
+ ConsulClientOptions options = new ConsulClientOptions().setHost("127.0.0.1").setPort(8500);
+
+ Watch.keyPrefix("apiCode_", vertx, options)
+ .setHandler(res -> {
+ if (res.succeeded()) {
+ // 删除
+ boolean isDel = (res.nextResult() == null || res.nextResult().getList() == null) && (res.prevResult() != null && res.prevResult().getList() != null);
+ if(isDel) {
+ log.info("isDel");
+ KeyValueList keyValueList = res.prevResult();
+ if(keyValueList != null && keyValueList.getList() != null) {
+ for(KeyValue keyValue : keyValueList.getList()) {
+ log.info("keyValue:{}", JSONObject.toJSONString(keyValue));
+ }
+ }
+ }
+ // 新增
+ boolean isAdd = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() == null || res.prevResult().getList() == null);
+ if(isAdd) {
+ log.info("isAdd");
+ KeyValueList keyValueList = res.nextResult();
+ if(keyValueList != null && keyValueList.getList() != null) {
+ for(KeyValue keyValue : keyValueList.getList()) {
+ log.info("keyValue:{}", JSONObject.toJSONString(keyValue));
+ }
+ }
+ }
+
+ // 修改
+ boolean isModify = (res.nextResult() != null && res.nextResult().getList() != null) && (res.prevResult() != null && res.prevResult().getList() != null);
+ if(isModify) {
+ log.info("isModify");
+ KeyValueList keyValueList = res.nextResult();
+ if(keyValueList != null && keyValueList.getList() != null) {
+ for(KeyValue keyValue : keyValueList.getList()) {
+ log.info("keyValue:{}", JSONObject.toJSONString(keyValue));
+ }
+ }
+ }
+ } else {
+ res.cause().printStackTrace();
+ }
+ })
+ .start();
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java
new file mode 100644
index 0000000..3195ee2
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/OpenParameterCheckHandlerImpl.java
@@ -0,0 +1,52 @@
+/*
+package com.sf.vertx.handle;
+
+import com.sf.vertx.api.pojo.ApiConfig;
+import com.sf.vertx.enums.GatewayError;
+import com.sf.vertx.exception.ServiceException;
+
+import com.sf.vertx.api.pojo.AppConfig;
+
+import com.sf.vertx.utils.AppUtils;
+import io.vertx.ext.web.RoutingContext;
+import lombok.extern.slf4j.Slf4j;
+
+import static com.sf.vertx.constans.SACConstants.*;
+
+@Slf4j
+public class OpenParameterCheckHandlerImpl implements ParameterCheckHandler {
+
+ @Override
+ public void handle(RoutingContext rc) {
+ try {
+ log.info("Enter OPEN Route");
+ // 判断OPEN模式, header不传递参数, 走域名映射
+ String domain = rc.request().authority().host();
+ log.info("request domain:{}", domain);
+ AppConfig appConfig = AppConfigHandler.getAppConfigByDomain(domain);
+ if (appConfig == null) {
+ rc.fail(new ServiceException(GatewayError.APP_SERVICE_NOT_FOUND));
+ return;
+ }
+ String apiCacheKey = appConfig.getAppCode() + CACHE_KEY_CONNECTOR + domain;
+ ApiConfig apiConfig = AppConfigHandler.getApicodeConfig(apiCacheKey);
+ if (apiConfig == null) {
+ rc.fail(new ServiceException(GatewayError.API_SERVICE_NOT_FOUND));
+ return;
+ }
+ // 设置应用配置、接口配置到上下文
+ AppUtils.setAppConfigToRoutingContext(appConfig,rc);
+ AppUtils.setApiConfigIntoRoutingContext(apiConfig,rc);
+ rc.put(API_SERVICE_TYPE,AppConfigHandler.getServiceTypeOpen(apiCacheKey));
+ // 将appcode和apicode设置到请求头,兼容原有逻辑
+ rc.request().headers().add(AppConfigHandler.getAppCodeHeaderKey(), appConfig.getAppCode());
+ rc.request().headers().add(AppConfigHandler.getApiCodeHeaderKey(), apiConfig.getApiCode());
+ } catch (Exception e) {
+ log.error("OpenParameterCheckHandlerImpl Error:",e);
+ rc.fail(new ServiceException(GatewayError.DEFAULT_SERVICE_ERROR));
+ return;
+ }
+ rc.next();
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java
new file mode 100644
index 0000000..a4f5610
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ParameterCheckHandler.java
@@ -0,0 +1,33 @@
+/*
+package com.sf.vertx.handle;
+
+import com.sf.vertx.enums.GatewayServiceType;
+import com.sf.vertx.enums.GatewayError;
+import com.sf.vertx.exception.ServiceException;
+import io.vertx.codegen.annotations.VertxGen;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+
+*/
+/***
+ * 入口参数校验
+ *
+ * @author xy
+ *
+ *//*
+
+@VertxGen
+public interface ParameterCheckHandler extends Handler {
+
+ static ParameterCheckHandler create(GatewayServiceType serviceType) {
+ switch (serviceType) {
+ case SAC:
+ return new SACParameterCheckHandlerImpl();
+ case OPEN:
+ return new OpenParameterCheckHandlerImpl();
+ default:
+ throw new ServiceException(GatewayError.INTERNAL_SERVER_ERROR);
+ }
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java
new file mode 100644
index 0000000..44cd8a5
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandler.java
@@ -0,0 +1,27 @@
+package com.sf.vertx.handle;
+
+import io.vertx.codegen.annotations.VertxGen;
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.httpproxy.HttpProxy;
+
+/**
+ * @author Emad Alblueshi
+ */
+
+@VertxGen
+public interface ProxyHandler extends Handler {
+
+ static ProxyHandler create(WebClient mainWebClient, HttpProxy httpProxy) {
+ return new ProxyHandlerImpl(mainWebClient, httpProxy);
+ }
+
+ static ProxyHandler create(HttpProxy httpProxy) {
+ return new ProxyHandlerImpl(httpProxy);
+ }
+
+ static ProxyHandler create(HttpProxy httpProxy, int port, String host) {
+ return new ProxyHandlerImpl(httpProxy, port, host);
+ }
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java
new file mode 100644
index 0000000..ba1b9d4
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/ProxyHandlerImpl.java
@@ -0,0 +1,35 @@
+package com.sf.vertx.handle;
+
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.client.WebClient;
+import io.vertx.httpproxy.HttpProxy;
+
+/**
+ * @author Emad Alblueshi
+ */
+public class ProxyHandlerImpl implements ProxyHandler {
+
+ private final HttpProxy httpProxy;
+ private WebClient mainWebClient;
+
+ public ProxyHandlerImpl(WebClient mainWebClient, HttpProxy httpProxy) {
+ this.httpProxy = httpProxy;
+ this.mainWebClient = mainWebClient;
+ }
+
+ public ProxyHandlerImpl(HttpProxy httpProxy) {
+ this.httpProxy = httpProxy;
+ }
+
+ public ProxyHandlerImpl(HttpProxy httpProxy, int port, String host) {
+ this.httpProxy = httpProxy.origin(port, host);
+ }
+
+ @Override
+ public void handle(RoutingContext ctx) {
+ // TODO 改造了这个地方
+ // httpProxy.handle(mainWebClient, ctx);
+ // 原始代码只有如下一句
+ httpProxy.handle(ctx.request());
+ }
+}
\ No newline at end of file
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java
new file mode 100644
index 0000000..df8e15b
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandler.java
@@ -0,0 +1,12 @@
+/*
+package com.sf.vertx.handle;
+
+import io.vertx.core.Handler;
+import io.vertx.ext.web.RoutingContext;
+
+public interface RestfulFailureHandler extends Handler {
+ static RestfulFailureHandlerImpl create() {
+ return new RestfulFailureHandlerImpl();
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java
new file mode 100644
index 0000000..202f12d
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/RestfulFailureHandlerImpl.java
@@ -0,0 +1,68 @@
+/*
+package com.sf.vertx.handle;
+
+import cn.hutool.core.util.StrUtil;
+import com.sf.vertx.enums.GatewayError;
+import com.sf.vertx.exception.MockException;
+import com.sf.vertx.exception.ServiceException;
+import com.sf.vertx.utils.AppUtils;
+
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.HttpException;
+import lombok.extern.slf4j.Slf4j;
+
+
+@Slf4j
+public class RestfulFailureHandlerImpl implements RestfulFailureHandler {
+
+ @Override
+ public void handle(RoutingContext frc) {
+ int statusCode = 500;
+ JsonObject errorJson;
+ // 网关的业务码
+ int gatewayServiceCode = GatewayError.DEFAULT_SERVICE_ERROR.getCode();
+ try {
+ Throwable failure = frc.failure();
+ log.info("failure:", failure);
+ if (failure instanceof HttpException) {
+ HttpException httpException = (HttpException) failure;
+ gatewayServiceCode = httpException.getStatusCode();
+ errorJson = AppUtils.getResponseJsonByGatewayError(GatewayError.getByCode(statusCode));
+ if (StrUtil.isNotBlank(httpException.getPayload())){
+ errorJson.put("msg",httpException.getPayload());
+ }
+ } else if (failure instanceof MockException) {
+ MockException mockException = (MockException) failure;
+ gatewayServiceCode = mockException.getStatusCode();
+ statusCode = mockException.getStatusCode();
+ errorJson = new JsonObject(mockException.getPayload());
+ }else if (failure instanceof ServiceException) {
+ ServiceException serviceException = (ServiceException) failure;
+ gatewayServiceCode = serviceException.getStatusCode();
+ // 业务异常,为网关业务错误,响应码设置为500,
+ GatewayError gatewayError = GatewayError.getByCode(serviceException.getStatusCode());
+ // 如果是被限流或熔断,直接返回限流或熔断响应
+ if (GatewayError.REQUEST_URL_RESTRICTED_BY_FLOW.equals(gatewayError)
+ || GatewayError.REQUEST_URL_IS_BROKEN.equals(gatewayError)){
+ errorJson = new JsonObject(serviceException.getPayload());
+ }else {
+ errorJson = AppUtils.getResponseJsonByGatewayError(gatewayError);
+ }
+ } else {
+ errorJson = AppUtils.getResponseJsonByGatewayError(GatewayError.getByCode(statusCode));
+ }
+
+ } catch (Exception e) {
+ log.error("RestfulFailureHandlerImpl.handle Error:",e);
+ errorJson = AppUtils.getResponseJsonByGatewayError(GatewayError.DEFAULT_SERVICE_ERROR);
+ }
+ frc.response().setChunked(true).setStatusCode(statusCode).putHeader("Content-Type", "application/json")
+ .putHeader(AppConfigHandler.sacResponseHeaderKey(), String.valueOf(gatewayServiceCode))
+ .end(errorJson.toBuffer());
+ }
+
+
+
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java
new file mode 100644
index 0000000..22d9714
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/handle/SACParameterCheckHandlerImpl.java
@@ -0,0 +1,52 @@
+/*
+package com.sf.vertx.handle;
+
+import cn.hutool.core.util.StrUtil;
+import com.sf.vertx.api.pojo.ApiConfig;
+import com.sf.vertx.api.pojo.AppConfig;
+import com.sf.vertx.enums.GatewayError;
+import com.sf.vertx.exception.ServiceException;
+import com.sf.vertx.utils.AppUtils;
+import io.vertx.ext.web.RoutingContext;
+import lombok.extern.slf4j.Slf4j;
+
+import static com.sf.vertx.constans.SACConstants.*;
+
+@Slf4j
+public class SACParameterCheckHandlerImpl implements ParameterCheckHandler {
+
+ @Override
+ public void handle(RoutingContext rc) {
+ try {
+ log.info("Enter SAC Route");
+ // SAC规范要求Header必须要同时包含appCode和apiCode
+ String appCode = rc.request().headers().get(AppConfigHandler.getAppCodeHeaderKey());
+ String apiCode = rc.request().headers().get(AppConfigHandler.getApiCodeHeaderKey());
+ if (StrUtil.isBlank(appCode) || StrUtil.isBlank(apiCode)) {
+ rc.fail(new ServiceException(GatewayError.PARAMETER_TRANSFER_ERROR));
+ return;
+ }
+ AppConfig appConfig = AppConfigHandler.getAppConfig(appCode);
+ if (appConfig == null) {
+ rc.fail(new ServiceException(GatewayError.APP_SERVICE_NOT_FOUND));
+ return;
+ }
+ String apiCacheKey = appConfig.getAppCode() + CACHE_KEY_CONNECTOR + apiCode;
+ ApiConfig apiConfig = AppConfigHandler.getApicodeConfig(apiCacheKey);
+ if (apiConfig == null) {
+ rc.fail(new ServiceException(GatewayError.API_SERVICE_NOT_FOUND));
+ return;
+ }
+ // 设置应用配置、接口配置到上下文
+ AppUtils.setAppConfigToRoutingContext(appConfig,rc);
+ AppUtils.setApiConfigIntoRoutingContext(apiConfig,rc);
+ rc.put(API_SERVICE_TYPE,AppConfigHandler.getServiceTypeOpen(apiCacheKey));
+ } catch (Exception e) {
+ log.error("SACParameterCheckHandlerImpl Error:",e);
+ rc.fail(new ServiceException(GatewayError.DEFAULT_SERVICE_ERROR));
+ return;
+ }
+ rc.next();
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java
new file mode 100644
index 0000000..9d0cf2b
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/DynamicBuildServer.java
@@ -0,0 +1,68 @@
+/*
+package com.sf.vertx.init;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import com.sf.vertx.constans.RedisKeyConfig;
+import com.sf.vertx.handle.AppConfigHandler;
+
+import lombok.extern.slf4j.Slf4j;
+
+*/
+/***
+ * 动态构建vertx服务
+ *
+ * @author xy
+ *
+ *//*
+
+@Slf4j
+@Order(value = 10)
+@Component
+public class DynamicBuildServer implements ApplicationRunner {
+ // TODO 后面可以和app挂钩
+ @Autowired
+ private SacVertxConfig sacVertxConfig;
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ @Autowired
+ private RedisKeyConfig redisKeyConfig;
+
+ @Override
+ public void run(ApplicationArguments args) throws Exception {
+ // 初始化redis key
+ redisKeyConfig.init();
+ AppConfigHandler.init(redisTemplate, sacVertxConfig);
+ // 从redis同步vertx配置
+ AppConfigHandler.initVertxConfig();
+
+ // 加载vertx、应用配置
+ startVertxService();
+ }
+
+ */
+/***
+ * 应用启动, 从redis读取配置,初始化vertx服务
+ *
+ * @throws Exception
+ *//*
+
+ private void startVertxService() throws Exception {
+ if(sacVertxConfig.getDeploymentMode() == 2) {
+ // 集群
+ AppConfigHandler.createHazelcastClusterVertx();
+ } else {
+ // 单机
+ AppConfigHandler.createVertx();
+ }
+ }
+
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java
new file mode 100644
index 0000000..6ee7d3f
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayConfigVerticle.java
@@ -0,0 +1,188 @@
+package com.sf.vertx.init;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.sf.vertx.enums.GatewayError;
+import io.vertx.config.ConfigRetriever;
+import io.vertx.config.ConfigRetrieverOptions;
+import io.vertx.config.ConfigStoreOptions;
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpHeaders;
+import io.vertx.core.http.HttpServerOptions;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.json.JsonObject;
+import io.vertx.core.net.JksOptions;
+import io.vertx.ext.web.Route;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.RoutingContext;
+import io.vertx.ext.web.handler.BodyHandler;
+import io.vertx.ext.web.handler.CorsHandler;
+import io.vertx.ext.web.handler.SessionHandler;
+import io.vertx.ext.web.sstore.ClusteredSessionStore;
+import io.vertx.ext.web.sstore.LocalSessionStore;
+import io.vertx.ext.web.sstore.SessionStore;
+import lombok.extern.slf4j.Slf4j;
+
+import java.text.MessageFormat;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 网关配置同步与管理
+ */
+@Slf4j
+public class GatewayConfigVerticle extends AbstractVerticle {
+
+ /**
+ * HTTP的路由服务
+ */
+ private static Router httpRouter = null;
+
+ private static VertxProperties vertxProperties;
+
+ /**
+ * 全局IP黑名单
+ */
+ private Set blackIpSet = new LinkedHashSet<>();
+
+ public void start() {
+ ConfigStoreOptions store = new ConfigStoreOptions()
+ .setType("file")
+ .setFormat("yaml")
+ .setConfig(new JsonObject()
+ .put("path", "application.yaml")
+ );
+
+ ConfigRetriever retriever = ConfigRetriever.create(vertx,
+ new ConfigRetrieverOptions().addStore(store));
+ retriever.getConfig(json -> {
+ if (json.succeeded()) {
+ doStart(json);
+ } else {
+ stop();
+ }
+ });
+
+
+ }
+
+
+ private void doStart(AsyncResult json) {
+ JsonObject config = json.result();
+ log.info("配置读取成功");
+ // 读取vertx配置
+ JsonObject vertxConfig = config.getJsonObject("vertx");
+ // 读取redis配置
+ JsonObject redisConfig = config.getJsonObject("redis");
+
+ //先初始化再发布Http服务
+ vertx.executeBlocking(() -> {
+ //顺序不能乱
+ try {
+ //初始化Redis
+ RedisManager.initRedisConfig(vertx, redisConfig, redisConnectionAsyncResult -> {
+ if (redisConnectionAsyncResult.succeeded()) {
+ log.info("GatewayConfig redis初始化成功");
+ } else {
+ log.error("GatewayConfig redis初始化失败", redisConnectionAsyncResult.cause());
+ stop();
+ }
+ });
+ } catch (Exception e) {
+ log.error("GatewayConfig redis初始化失败:", e);
+ stop();
+ }
+ return true;
+ }).onComplete(ar -> {
+ // 启动服务
+ if (ar.succeeded()) {
+ vertxProperties = vertxConfig.mapTo(VertxProperties.class);
+ blackIpSet = new LinkedHashSet<>(List.of(vertxProperties.getBlackIP()));
+ createHttpServer(createServerAR ->{
+ if (createServerAR.succeeded()) {
+ log.info("GatewayConfig 启动服务成功");
+ // 添加路由
+ Route addAppConfigRoute = httpRouter.post("/vertx/app/config");
+ addAppConfigRoute.handler(BodyHandler.create(false))
+ .handler(ctx ->{
+ String s = ctx.body().asString();
+ log.info(s);
+ JSONObject jsonre = new JSONObject();
+ jsonre.put("code", 200);
+ jsonre.put("msg", "success");
+ ctx.response()
+ .putHeader("Content-Type", "application/json")
+ .end(jsonre.toJSONString());
+ });
+
+ } else {
+ log.error("GatewayConfig 启动服务失败", createServerAR.cause());
+ stop();
+ }
+ });
+ } else {
+ log.error("GatewayConfig 启动服务失败:", ar.cause());
+ stop();
+ }
+ });
+ }
+
+
+ public void stop() {
+ log.info("close GatewayConfigVerticle !");
+ vertx.close();
+ }
+
+ /**
+ * 创建http服务器
+ *
+ * @param createHttp
+ */
+ public void createHttpServer(Handler> createHttp) {
+ // 所有ip都能访问
+ HttpServerOptions httpServerOptions = new HttpServerOptions().setHost("0.0.0.0");
+ if (vertxProperties.isSSL()) {
+ httpServerOptions.setSsl(true)
+ .setKeyStoreOptions(new JksOptions().setPassword("changeit").setPath("keystore.jks"));
+ }
+ httpRouter = Router.router(vertx);
+ httpRouter.route().handler(this::filterBlackIP);
+ httpRouter.route().handler(CorsHandler.create().addRelativeOrigin(".*"));
+ // 创建http服务器
+ vertx.createHttpServer(httpServerOptions)
+ .requestHandler(httpRouter)
+ .listen(vertxProperties.getConfigPort(), res -> {
+ if (res.succeeded()) {
+ log.info("GatewayConfigServer Running on port {} by HTTP", vertxProperties.getConfigPort());
+ createHttp.handle(Future.succeededFuture());
+ } else {
+ log.error("create HTTP Server (GatewayConfigServer) failed : GatewayConfigServer Running on port {} by HTTP", vertxProperties.getConfigPort());
+ createHttp.handle(Future.failedFuture(res.cause()));
+ }
+ });
+ }
+
+ /**
+ * 过滤黑名单
+ *
+ * @param rct
+ */
+ public void filterBlackIP(RoutingContext rct) {
+
+ String host = rct.request().remoteAddress().host();
+ if (blackIpSet.contains(host)) {
+ HttpServerResponse response = rct.response();
+ response.putHeader(HttpHeaders.CONTENT_TYPE, "text/html");
+ response.setStatusCode(GatewayError.FORBIDDEN.getCode());
+ response.setStatusMessage(GatewayError.FORBIDDEN.getReasonPhrase());
+ response.end("" +
+ "you can't access this service
" +
+ "");
+ } else {
+ rct.next();
+ }
+ }
+}
\ No newline at end of file
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java
new file mode 100644
index 0000000..b3d6c9e
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/GatewayRPCVerticle.java
@@ -0,0 +1,79 @@
+package com.sf.vertx.init;
+
+import io.vertx.config.ConfigRetriever;
+import io.vertx.config.ConfigRetrieverOptions;
+import io.vertx.config.ConfigStoreOptions;
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.http.HttpServer;
+import io.vertx.core.http.HttpServerResponse;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.Router;
+import io.vertx.ext.web.handler.BodyHandler;
+import io.vertx.ext.web.handler.StaticHandler;
+import io.vertx.redis.client.impl.RedisClient;
+
+
+/**
+ * 网关RPC支持
+ */
+public class GatewayRPCVerticle extends AbstractVerticle {
+
+
+
+
+ public void start() {
+ ConfigStoreOptions store = new ConfigStoreOptions()
+ .setType("file")
+ .setFormat("yaml")
+ .setConfig(new JsonObject()
+ .put("path", "application.yaml")
+ );
+
+ ConfigRetriever retriever = ConfigRetriever.create(vertx,
+ new ConfigRetrieverOptions().addStore(store));
+ // Create redisClient
+ JsonObject cachedConfig = retriever.getCachedConfig();
+
+ //RedisOptions config = new RedisOptions()
+ //.("127.0.0.1");
+
+ //redisClient = new RedisClient(vertx, config);
+ }
+
+ private void startWebApp(Handler> next) {
+ // Create a router object.
+ Router router = Router.router(vertx);
+
+ // Bind "/" to our hello message.
+ router.route("/").handler(routingContext -> {
+ HttpServerResponse response = routingContext.response();
+ response
+ .putHeader("content-type", "text/html")
+ .end("Hello from my first Vert.x 3 application
");
+ });
+
+ router.route("/assets/*").handler(StaticHandler.create("assets"));
+
+ /* router.get("/api/whiskies").handler(this::getAll);
+ router.route("/api/whiskies*").handler(BodyHandler.create());
+ router.post("/api/whiskies").handler(this::addOne);
+ router.get("/api/whiskies/:id").handler(this::getOne);
+ router.put("/api/whiskies/:id").handler(this::updateOne);
+ router.delete("/api/whiskies/:id").handler(this::deleteOne);*/
+
+
+ // Create the HTTP server and pass the "accept" method to the request handler.
+ vertx
+ .createHttpServer()
+ .listen(
+ // Retrieve the port from the configuration,
+ // default to 8080.
+ config().getInteger("http.port", 8080),
+ next::handle
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java
new file mode 100644
index 0000000..7f054ec
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisManager.java
@@ -0,0 +1,62 @@
+package com.sf.vertx.init;
+
+import cn.hutool.core.util.StrUtil;
+import io.vertx.core.AsyncResult;
+import io.vertx.core.Future;
+import io.vertx.core.Handler;
+import io.vertx.core.Vertx;
+import io.vertx.core.json.JsonArray;
+import io.vertx.core.json.JsonObject;
+import io.vertx.redis.client.*;
+import io.vertx.redis.client.impl.RedisClient;
+
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * 功能描述:
+ *
+ * @author a_kun
+ * @date 2024/5/27 16:53
+ */
+public class RedisManager {
+
+ private static RedisAPI redisAPI;
+ private static Redis redis;
+
+ private static RedisProperties redisProperties;
+
+ public static void initRedisConfig(Vertx vertx, JsonObject redisConfig, Handler> handler) {
+ try {
+ if (redis == null) {
+ redisProperties = redisConfig.mapTo(RedisProperties.class);
+ //解析配置
+ RedisOptions options = new RedisOptions()
+ .setType(redisProperties.getClientType())
+ .setPoolName(redisProperties.getPoolName())
+ .setMaxPoolSize(redisProperties.getMaxPoolSize())
+ .setMaxPoolWaiting(redisProperties.getMaxPoolWaiting())
+ .setPoolCleanerInterval(redisProperties.getPoolCleanerInterval());
+ // password
+ if (StrUtil.isNotBlank(redisProperties.getPassword())) {
+ options.setPassword(redisProperties.getPassword());
+ }
+ // connect address [redis://localhost:6379/0, redis://localhost:6779/1]
+ for (String url : redisProperties.getUrls()) {
+ options.addConnectionString(url);
+ }
+ // sentinel
+ if (redisProperties.getClientType().equals(RedisClientType.SENTINEL)) {
+ options.setRole(redisProperties.getRole()).setMasterName(redisProperties.getMasterName());
+ }
+ //创建redis实例
+ redis = Redis.createClient(vertx, options);
+ redisAPI = RedisAPI.api(redis);
+ }
+ handler.handle(Future.succeededFuture());
+ } catch (Exception e) {
+ handler.handle(Future.failedFuture(e));
+ }
+ }
+
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java
new file mode 100644
index 0000000..1a03310
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/RedisProperties.java
@@ -0,0 +1,26 @@
+package com.sf.vertx.init;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.vertx.redis.client.RedisClientType;
+import io.vertx.redis.client.RedisRole;
+import lombok.Data;
+
+/**
+ * Redis配置
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class RedisProperties {
+ private RedisClientType clientType = RedisClientType.STANDALONE;
+ private String[] urls;
+ private String password;
+ private String poolName = "vertx-redis";
+ private int poolCleanerInterval = 30000;
+ private int maxPoolSize = 8;
+ private int maxPoolWaiting = 32;
+ private String masterName = "redis_master";
+ private RedisRole role = RedisRole.MASTER;
+
+ private Integer expireTime = 60 * 60;
+
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java
new file mode 100644
index 0000000..ddc5991
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/SacVertxConfig.java
@@ -0,0 +1,46 @@
+/*
+package com.sf.vertx.init;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import lombok.Data;
+
+*/
+/***
+ * 配置文件
+ * @author xy
+ *
+ *//*
+
+//@Component
+@Data
+public class SacVertxConfig {
+ @Value("${server.vertx.server.default.port:80}")
+ private Integer port;
+
+ @Value("${server.vertx.cluster.ip:127.0.0.1}")
+ private String clusterIp;
+
+ @Value("${server.vertx.cluster.networkPort:5701}")
+ private Integer networkPort;
+
+ @Value("${server.vertx.cluster.portAutoIncrement:false}")
+ private boolean portAutoIncrement;
+
+ @Value("${server.vertx.sacResponseHeaderKey:sacErrorCode}")
+ private String sacResponseHeaderKey;
+
+ @Value("${server.vertx.rpcUri:/rpc}")
+ private String rpcUri;
+
+ @Value("${server.vertx.isSSL:false}")
+ private boolean isSSLs;
+
+ @Value("${server.vertx.deploymentMode:1}")
+ private Integer deploymentMode;
+
+ @Value("${server.vertx.cluster.clusterName:sac}")
+ private String clusterName;
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java
new file mode 100644
index 0000000..53d1206
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/init/VertxProperties.java
@@ -0,0 +1,26 @@
+package com.sf.vertx.init;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.vertx.core.json.JsonObject;
+import lombok.Data;
+
+import java.util.Map;
+
+
+/**
+ * Redis配置
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class VertxProperties {
+ private int deploymentMode = 1;
+ private boolean isSSL = false;
+ private int rpcPort = 80;
+ private String rpcUri;
+ private String sacResponseHeaderKey;
+ private String environment;
+ private int configPort = 5566;
+ private Map cluster;
+ private String[] blackIP;// 黑名单
+
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java
new file mode 100644
index 0000000..697fc60
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/ClusterEventMsg.java
@@ -0,0 +1,13 @@
+package com.sf.vertx.pojo;
+
+import java.io.Serializable;
+
+import lombok.Data;
+
+@Data
+public class ClusterEventMsg implements Serializable{
+ private static final long serialVersionUID = -3380175299815557039L;
+ private int type; // 1: app
+ private int operation; // 1:新增,修改,3:删除
+ private String appCode;
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java
new file mode 100644
index 0000000..c5c13b8
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/pojo/SacCurrentLimiting.java
@@ -0,0 +1,13 @@
+package com.sf.vertx.pojo;
+
+import com.sf.vertx.api.pojo.Strategy;
+
+import io.github.resilience4j.ratelimiter.RateLimiterRegistry;
+import lombok.Data;
+
+@Data
+public class SacCurrentLimiting {
+
+ private RateLimiterRegistry registry;
+ private Strategy strategy;
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java
new file mode 100644
index 0000000..f910e7e
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/AesUtils.java
@@ -0,0 +1,84 @@
+package com.sf.vertx.security;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * 本类采用对称加密 加密算法:{@link #PADDINIG_MODE}
+ * {@link #RESULT_TYPE} 密文结果:1=base64 2=hex
+ */
+public class AesUtils {
+ /**
+ * 整平方法
+ */
+ private static final int RESULT_TYPE = 2;
+
+ /**
+ * 加密方法
+ */
+ private static final String AES_ALGORITHM = "AES";
+
+ /**
+ * 填充方法
+ */
+ private static final String PADDINIG_MODE = "AES/CBC/PKCS5Padding";
+
+ /**
+ * 偏移量
+ */
+ private static final byte[] IV = "0000000000000000".getBytes();
+
+ public static String encrypt(String s, String k) throws Exception {
+ SecretKeySpec key = new SecretKeySpec(StringUtils.getBytes(k), AES_ALGORITHM);
+ byte[] data = encrypt(StringUtils.getBytes(s), key);
+ String result;
+ switch (RESULT_TYPE) {
+ case 1:
+ result = Base64Utils.encode(data);
+ break;
+ case 2:
+ result = HexUtils.bytes2Hex(data);
+ break;
+ default:
+ throw new Exception("Unsupport Result Type");
+ }
+ return result;
+ }
+
+ public static String decrypt(String s, String k) throws Exception {
+ SecretKeySpec key = new SecretKeySpec(StringUtils.getBytes(k), AES_ALGORITHM);
+ byte[] data;
+ switch (RESULT_TYPE) {
+ case 1:
+ data = Base64Utils.decode(s);
+ break;
+ case 2:
+ data = HexUtils.hex2Bytes(s);
+ break;
+ default:
+ throw new Exception("Unsupport Result Type");
+ }
+ return StringUtils.bytes2String(decrypt(data, key));
+ }
+
+ private static byte[] encrypt(byte[] data, SecretKeySpec keySpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+ IvParameterSpec ivspec = new IvParameterSpec(IV);
+ Cipher cipher = Cipher.getInstance(PADDINIG_MODE);
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec);
+ return cipher.doFinal(data);
+ }
+
+ private static byte[] decrypt(byte[] data, SecretKeySpec keySpec) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+ IvParameterSpec ivspec = new IvParameterSpec(IV);
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivspec);
+ return cipher.doFinal(data);
+ }
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java
new file mode 100644
index 0000000..02d002f
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/Base64Utils.java
@@ -0,0 +1,31 @@
+package com.sf.vertx.security;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+
+/**
+ * 将Base64字符串和字节数组互转
+ */
+public class Base64Utils {
+
+ /**
+ * 将字节数组编码为base64字符串
+ *
+ * @param b 字节数组
+ * @return String 字符串
+ */
+ public static String encode(byte[] b) {
+ return Base64.getEncoder().encodeToString(b);
+ }
+
+ /**
+ * 将字符串转base64为字节数组
+ *
+ * @param s 字符串
+ * @return byte[] 字节数组
+ */
+ public static byte[] decode(String s) {
+ return Base64.getDecoder().decode(s);
+ }
+
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java
new file mode 100644
index 0000000..c3842c8
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/HexUtils.java
@@ -0,0 +1,53 @@
+package com.sf.vertx.security;
+
+/**
+ * 将字节数组和HEX字符串进行互转
+ */
+public class HexUtils {
+ private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ /**
+ * 字节数组转字符串
+ *
+ * @param byteArr 字节数组
+ * @return 字符串
+ */
+ public static String bytes2Hex(byte[] byteArr) {
+ StringBuilder sb = new StringBuilder(byteArr.length);
+ for (byte b : byteArr) {
+ String sTemp = Integer.toHexString(255 & b);
+ if (sTemp.length() < 2) {
+ sb.append(0);
+ }
+ sb.append(sTemp.toUpperCase());
+ }
+ return sb.toString();
+ }
+
+ /**
+ * HEX字符串转字节数组
+ *
+ * @param str 字符串
+ * @return 字节数组
+ */
+ public static byte[] hex2Bytes(String str) {
+ if (str == null) {
+ return null;
+ } else {
+ char[] hex = str.toCharArray();
+ int length = hex.length / 2;
+ byte[] raw = new byte[length];
+
+ for (int i = 0; i < length; ++i) {
+ int high = Character.digit(hex[i * 2], 16);
+ int low = Character.digit(hex[i * 2 + 1], 16);
+ int value = high << 4 | low;
+ if (value > 127) {
+ value -= 256;
+ }
+ raw[i] = (byte) value;
+ }
+ return raw;
+ }
+ }
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java
new file mode 100644
index 0000000..3fb9e61
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MainSecurity.java
@@ -0,0 +1,84 @@
+/*
+package com.sf.vertx.security;
+
+import java.util.logging.Logger;
+
+*/
+/**
+ * 本项目中真正对外提供服务的工具类
+ *//*
+
+public class MainSecurity {
+ private static final Logger LOGGER = Logger.getLogger(MainSecurity.class.getName());
+
+ */
+/**
+ * 加密,当失败的时候,返回空字符串
+ *
+ * @param content
+ * @param pubKey
+ * @return
+ *//*
+
+ public static String rsaEncrypt(String content, String pubKey) {
+ try {
+ return RSAUtil.encrypt(content, pubKey);
+ } catch (Exception e) {
+ LOGGER.info("RSA加密失败");
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static String rsaDecrypt(String content, String priKey) {
+ try {
+ return RSAUtil.decrypt(content, priKey);
+ } catch (Exception e) {
+ LOGGER.info("RSA解密失败");
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static String aesEncrypt(String content, String key) {
+ try {
+ return AesUtils.encrypt(content, key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOGGER.info("AES加密失败");
+ return null;
+ }
+ }
+
+ public static String aesDecrypt(String content, String key) {
+ try {
+ return AesUtils.decrypt(content, key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOGGER.info("AES解密失败");
+ return null;
+ }
+ }
+
+ public static String sign(String content) {
+ try {
+ return MessageDigest.md5(content);
+ } catch (Exception e) {
+ e.printStackTrace();
+ LOGGER.info("MD5加密失败");
+ return null;
+ }
+ }
+
+ public static void main(String[] args) {
+// System.out.println(aesEncrypt("{\n"
+// + " \"errorCode\": \"中文\",\n"
+// + " \"result\": 0,\n"
+// + " \"data\": {\n"
+// + " \"username\" : \"测试\"\n"
+// + " }\n"
+// + "}", "dadddsdfadfadsfa33323223"));
+// System.out.println(aesDecrypt("59A69B6BBCF046C3CF9953C5CC078CC638602D454BBCE8CF8F0DA6AF1F3A4707686263C834A612C5C6F22D9F897B13B434A53E32AAD4036E12A5098565AB1AD352B400FC23354ECE977DDC670F793992D7F884264A9689B000E37157B4D41351", "dadddsdfadfadsfa33323223"));
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java
new file mode 100644
index 0000000..e570d2e
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/MessageDigest.java
@@ -0,0 +1,47 @@
+package com.sf.vertx.security;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.NoSuchAlgorithmException;
+
+public class MessageDigest {
+
+ public static String md5(String text) throws NoSuchAlgorithmException {
+ java.security.MessageDigest digest = java.security.MessageDigest.getInstance("md5");
+ byte[] buffer = digest.digest(text.getBytes());
+ StringBuilder sb = new StringBuilder();
+ for (byte b : buffer) {
+ int a = b & 0xff;
+ String hex = Integer.toHexString(a);
+
+ if (hex.length() == 1) {
+ hex = 0 + hex;
+ }
+ sb.append(hex);
+ }
+ return sb.toString();
+ }
+
+ private static String hmacSha1(String data, String key, int type) throws Exception {
+ //根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
+ SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
+ //生成一个指定 Mac 算法 的 Mac 对象
+ Mac mac = Mac.getInstance("HmacSHA1");
+ //用给定密钥初始化 Mac 对象
+ mac.init(signinKey);
+ //完成 Mac 操作
+ byte[] rawHmac = mac.doFinal(StringUtils.getBytes(data));
+ String result;
+ switch (type) {
+ case 1:
+ result = Base64Utils.encode(rawHmac);
+ break;
+ case 2:
+ result = HexUtils.bytes2Hex(rawHmac);
+ break;
+ default:
+ throw new Exception("Unsupport Type");
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java
new file mode 100644
index 0000000..5ab966d
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSA2Utils.java
@@ -0,0 +1,276 @@
+//package com.sf.vertx.security;
+//
+//
+//import javax.crypto.BadPaddingException;
+//import javax.crypto.Cipher;
+//import javax.crypto.IllegalBlockSizeException;
+//import javax.crypto.NoSuchPaddingException;
+//import java.io.ByteArrayOutputStream;
+//import java.io.IOException;
+//import java.io.UnsupportedEncodingException;
+//import java.security.*;
+//import java.security.spec.InvalidKeySpecException;
+//import java.security.spec.PKCS8EncodedKeySpec;
+//import java.security.spec.X509EncodedKeySpec;
+//import java.util.List;
+//
+///**
+// * 本类进行非对称加密,不推荐使用非对称加密对长字符串进行加密或者解密,徒增资源消耗,另外由于长度限制,过长的字符串的加密和解密会使用循环,对数据分段加密;本类采用的
+// * 密钥字符串均为Base64加密后的
+// * 另外所有异常都会抛出
+// * 下面将会列举几个可以自定义或者暴露出去的接口和参数
+// * {@link #IS_LONG_TEXT} 是否否对长文本处理
+// * {@link #RESULT_TYPE} 密文结果:1=base64 2=hex
+// * {@link #RSA_ALGORITHM} RSA算法
+// * {@link #encrypt(String, String)} 加密方法
+// * {@link #decrypt(String, String)} 解密方法
+// * {@link #getKeyPair} 解密方法
+// */
+//public class RSA2Utils {
+// /**
+// * 是否对长文本加密;请参照{@link #MAX_DECRYPT_BLOCK}和{@link #MAX_ENCRYPT_BLOCK}
+// */
+// private static final boolean IS_LONG_TEXT = true;
+// /**
+// * 结果类型
+// */
+// private static final int RESULT_TYPE = 2;
+// /**
+// * RSA 算法
+// */
+// private static final String RSA_ALGORITHM = "RSA";
+// /**
+// * 长文本解密块大小
+// */
+// private static final int MAX_DECRYPT_BLOCK = 128;
+// /**
+// * 长文本加密块大小
+// */
+// private static final int MAX_ENCRYPT_BLOCK = 117;
+// /**
+// * KyeSize
+// */
+// private static final int KEY_SIZE = 2048;
+//
+// /**
+// * 加密
+// *
+// * @param content 待加密的字符串
+// * @param pubKey 公钥字符串
+// * @return 加密后的文本
+// * @throws Exception 异常
+// */
+// public static String encrypt(String content, String pubKey) throws Exception {
+// byte[] data = StringUtils.getBytes(content);
+// PublicKey publicKey = string2PubKey(pubKey);
+// byte[] resultArr;
+// if (IS_LONG_TEXT) {
+// resultArr = encryptLongStr(data, publicKey);
+// } else {
+// resultArr = encrypt(data, publicKey);
+// }
+// String result;
+// switch (RESULT_TYPE) {
+// case 1:
+// result = Base64Utils.encode(resultArr);
+// break;
+// case 2:
+// result = HexUtils.bytes2Hex(resultArr);
+// break;
+// default:
+// throw new Exception("Unsupport result type");
+// }
+// return result;
+// }
+//
+// /**
+// * @param content 密文内容
+// * @param priKey 私钥
+// * @return 解密后的字符串
+// * @throws Exception 异常
+// */
+// public static String decrypt(String content, String priKey) throws Exception {
+// byte[] data;
+// switch (RESULT_TYPE) {
+// case 1:
+// data = Base64Utils.decode(content);
+// break;
+// case 2:
+// data = HexUtils.hex2Bytes(content);
+// break;
+// default:
+// throw new Exception("Unsupport result type");
+// }
+// PrivateKey privateKey = string2PrivateKey(priKey);
+// byte[] result;
+// if (IS_LONG_TEXT) {
+// result = decryptLongStr(data, privateKey);
+// } else {
+// result = decrypt(privateKey, data);
+// }
+// return StringUtils.bytes2String(result);
+// }
+//
+// /**
+// * 响应公私钥对
+// *
+// * @return 0号 公钥 1号 私钥
+// * @throws NoSuchAlgorithmException 异常
+// */
+// public static List getKeyPair() throws NoSuchAlgorithmException {
+// KeyPair keyPairObj = getKeyPairObj();
+// return List.of(Base64Utils.encode(keyPairObj.getPublic().getEncoded()), Base64Utils.encode(keyPairObj.getPrivate().getEncoded()));
+// }
+//
+// /**
+// * 将公钥字符串转化为对象
+// *
+// * @param s base64字符串
+// * @return 公钥
+// * @throws NoSuchAlgorithmException 异常
+// * @throws UnsupportedEncodingException 异常
+// * @throws InvalidKeySpecException 异常
+// */
+// private static PublicKey string2PubKey(String s) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeySpecException {
+// KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
+// X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Utils.decode(s));
+// return keyFactory.generatePublic(keySpec);
+// }
+//
+// /**
+// * 对段字符串进行加密
+// *
+// * @param bytes 字节数组
+// * @param publicKey 公钥
+// * @return 加密后的数组
+// * @throws InvalidKeyException 异常
+// * @throws BadPaddingException 异常
+// * @throws IllegalBlockSizeException 异常
+// * @throws NoSuchPaddingException 异常
+// * @throws NoSuchAlgorithmException 异常
+// */
+// private static byte[] encrypt(byte[] bytes, PublicKey publicKey) throws InvalidKeyException, BadPaddingException, IllegalBlockSizeException, NoSuchPaddingException, NoSuchAlgorithmException {
+// Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+// cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+// return cipher.doFinal(bytes);
+// }
+//
+// /**
+// * 对长字符串进行加密
+// *
+// * @param bytes 字节数组
+// * @param publicKey 公钥
+// * @return 加密后的数组
+// * @throws NoSuchPaddingException 异常
+// * @throws NoSuchAlgorithmException 异常
+// * @throws InvalidKeyException 异常
+// */
+// private static byte[] encryptLongStr(byte[] bytes, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException {
+// Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+// cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+// int inputLen = bytes.length;
+// byte[] encryptedData = new byte[0];
+// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+// int offSet = 0;
+// byte[] cache;
+// int i = 0;
+// // 对数据分段加密
+// while (inputLen - offSet > 0) {
+// if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
+// cache = cipher.doFinal(bytes, offSet, MAX_ENCRYPT_BLOCK);
+// } else {
+// cache = cipher.doFinal(bytes, offSet, inputLen - offSet);
+// }
+// out.write(cache, 0, cache.length);
+// i++;
+// offSet = i * MAX_ENCRYPT_BLOCK;
+// }
+// encryptedData = out.toByteArray();
+// } catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
+// e.printStackTrace();
+// }
+// return encryptedData;
+// }
+//
+// /**
+// * 私钥字符串转为私钥对象
+// *
+// * @param priStr 私钥字符串
+// * @return 私钥对象
+// * @throws NoSuchAlgorithmException 异常
+// * @throws InvalidKeySpecException 异常
+// */
+// private static PrivateKey string2PrivateKey(String priStr) throws NoSuchAlgorithmException, InvalidKeySpecException {
+// PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Utils.decode(priStr));
+// KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
+// return keyFactory.generatePrivate(keySpec);
+// }
+//
+// /**
+// * 解密
+// *
+// * @param privateKey 私钥
+// * @param bytes 字节数组
+// * @return 解密后的字节数组
+// * @throws NoSuchPaddingException 异常
+// * @throws NoSuchAlgorithmException 异常
+// * @throws BadPaddingException 异常
+// * @throws IllegalBlockSizeException 异常
+// * @throws InvalidKeyException 异常
+// */
+// public static byte[] decrypt(PrivateKey privateKey, byte[] bytes) throws NoSuchPaddingException, NoSuchAlgorithmException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException {
+// Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
+// cipher.init(Cipher.DECRYPT_MODE, privateKey);
+// return cipher.doFinal(bytes);
+// }
+//
+// /**
+// * 解密
+// *
+// * @param data 解密前的字节数组
+// * @param privateKey 私钥
+// * @return 解密后的字节数组
+// * @throws InvalidKeyException 异常
+// * @throws NoSuchPaddingException 异常
+// * @throws NoSuchAlgorithmException 异常
+// */
+// public static byte[] decryptLongStr(byte[] data, PrivateKey privateKey) throws InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
+// Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
+// cipher.init(Cipher.DECRYPT_MODE, privateKey);
+// int inputLen = data.length;
+// byte[] result = new byte[0];
+// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+// int offSet = 0;
+// byte[] cache;
+// int i = 0;
+// // 对数据分段解密
+// while (inputLen - offSet > 0) {
+// if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+// cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
+// } else {
+// cache = cipher.doFinal(data, offSet, inputLen - offSet);
+// }
+// out.write(cache, 0, cache.length);
+// i++;
+// offSet = i * MAX_DECRYPT_BLOCK;
+// }
+// result = out.toByteArray();
+// } catch (BadPaddingException | IllegalBlockSizeException | IOException e) {
+// e.printStackTrace();
+// }
+// return result;
+// }
+//
+// /**
+// * 获得一堆公私钥
+// *
+// * @return KeyPair对象
+// * @throws NoSuchAlgorithmException 异常
+// */
+// private static KeyPair getKeyPairObj() throws NoSuchAlgorithmException {
+// KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
+// SecureRandom secureRandom = new SecureRandom(StringUtils.getBytes(String.valueOf(System.currentTimeMillis())));
+// keyPairGenerator.initialize(KEY_SIZE, secureRandom);
+// return keyPairGenerator.genKeyPair();
+// }
+//}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java
new file mode 100644
index 0000000..f3a0047
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/RSAUtil.java
@@ -0,0 +1,154 @@
+/*
+package com.sf.vertx.security;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.Cipher;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.ArrayUtils;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class RSAUtil {
+
+ // RSA最大加密明文大小
+ private static final int MAX_ENCRYPT_BLOCK = 117;
+
+ // RSA最大解密密文大小
+ private static final int MAX_DECRYPT_BLOCK = 128;
+
+ private RSAUtil() {
+ }
+
+ */
+/**
+ * 获取公钥和私钥对,key为公钥,value为私钥
+ *
+ * @return
+ * @throws NoSuchAlgorithmException
+ *//*
+
+ public static Map genKeyPair() throws NoSuchAlgorithmException {
+ KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
+ keyPairGen.initialize(1024, new SecureRandom());
+ KeyPair keyPair = keyPairGen.generateKeyPair();
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+ String publicKeyString = null;
+ String privateKeyString = null;
+
+ try {
+ publicKeyString = new String(Base64.encodeBase64(publicKey.getEncoded()), "UTF-8");
+ privateKeyString = new String(Base64.encodeBase64(privateKey.getEncoded()), "UTF-8");
+ } catch (UnsupportedEncodingException var7) {
+ var7.printStackTrace();
+ }
+
+ Map keyPairMap = new HashMap<>();
+ keyPairMap.put("publicKey", publicKeyString);
+ keyPairMap.put("privateKey", privateKeyString);
+ return keyPairMap;
+ }
+
+ public static String encrypt(String str, String publicKey) throws Exception {
+ byte[] decoded = Base64.decodeBase64(publicKey);
+ RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
+ .generatePublic(new X509EncodedKeySpec(decoded));
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(1, pubKey);
+ // 分段加密
+ // URLEncoder编码解决中文乱码问题
+ byte[] data = URLEncoder.encode(str, "UTF-8").getBytes("UTF-8");
+ // 加密时超过117字节就报错。为此采用分段加密的办法来加密
+ byte[] enBytes = null;
+ for (int i = 0; i < data.length; i += MAX_ENCRYPT_BLOCK) {
+ // 注意要使用2的倍数,否则会出现加密后的内容再解密时为乱码
+ byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(data, i, i + MAX_ENCRYPT_BLOCK));
+ enBytes = ArrayUtils.addAll(enBytes, doFinal);
+ }
+ return Base64.encodeBase64String(enBytes);
+ }
+
+ */
+/**
+ * 公钥分段解密
+ *
+ * @param str
+ * @param privateKey
+ * @return
+ * @throws Exception
+ *//*
+
+ public static String decrypt(String str, String privateKey) throws Exception {
+ // 获取公钥
+ byte[] decoded = Base64.decodeBase64(privateKey);
+ RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA")
+ .generatePrivate(new PKCS8EncodedKeySpec(decoded));
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
+ cipher.init(2, priKey);
+ byte[] data = Base64.decodeBase64(str.getBytes("UTF-8"));
+
+ // 返回UTF-8编码的解密信息
+ int inputLen = data.length;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int offSet = 0;
+ byte[] cache;
+ int i = 0;
+ // 对数据分段解密
+ while (inputLen - offSet > 0) {
+ if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
+ cache = cipher.doFinal(data, offSet, MAX_DECRYPT_BLOCK);
+ } else {
+ cache = cipher.doFinal(data, offSet, inputLen - offSet);
+ }
+ out.write(cache, 0, cache.length);
+ i++;
+ offSet = i * 128;
+ }
+ byte[] decryptedData = out.toByteArray();
+ out.close();
+ return URLDecoder.decode(new String(decryptedData, "UTF-8"), "UTF-8");
+ }
+
+ public static void main(String[] args) throws Exception {
+ Map keyMap = genKeyPair();
+ String publicKey = keyMap.get("publicKey");
+ String privateKey = keyMap.get("privateKey");
+ privateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIBjjfp2vkdIDzw9vTzei9S1Gg8KLfPWNYfESwgqGvPY/ps3rIfgPH4na3qy957AY/zHExjaPGbbBRBRPV3xg1vrccr0oZLPiC5n7+4Z5TR6wYiPOn3osSjCweUJ7LttOTAr8sqvaHlrDbpZD+ZoOiB0TEWWD9TvI4pK0sVs7JYRAgMBAAECgYAJ08T3c80Y/u2mnGmunfC5U3LnDY4KpN30Uky1d2aYfWawqhRnUp1CwUDvc4EzajHFFJZUP8khjNhwgS1nsk+t9fnz/GJSS2ZYIOn/i1WEKCJvCILuFpx9tqQM1I9EdueeW1VQgm24o6vbBTP6JzoRXN/l/dAGldluY+Y2JclVeQJBAIyZdxIo1G7qs2nbKkynQaPY/ogmn7VLTWwowri1bhWt0zzcDu3/TgLLPldWVwYUuRChyVvX3cuQB0ICwiKTB3kCQQDpxGufjUgalAZwe+RCxIDriKVvYmB/krk/escAR39Ya6Od7nHSbmdBUmc7vjLbVN3BsYWZrlxHnGelEetebqVZAkBJrxvR7of2YRYJwgxXA8jIv64VWHiWoJJAvtPdzWeWAPUVjhZc8FHH8RAI4XzV+QJMDx3h/i2Ew0SqeZuYVwmxAkBh2T3TQyfzOBKZ8sHQ0L/F1ySoQt1xiNDRqWqyyzqaoDOUX8J0+pFt3jgn4a0X8aYA9XWepkUqFGWtyppipJ3BAkEAigeRnLS/J4Z6inWy4XJfFrggYvtQvWQnGfjHNQijm7+c8Nb+gs2EjbkEHg13yFUpBa3sVPN+aOhP2R41t4EywA==";
+ publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCAY436dr5HSA88Pb083ovUtRoPCi3z1jWHxEsIKhrz2P6bN6yH4Dx+J2t6sveewGP8xxMY2jxm2wUQUT1d8YNb63HK9KGSz4guZ+/uGeU0esGIjzp96LEowsHlCey7bTkwK/LKr2h5aw26WQ/maDogdExFlg/U7yOKStLFbOyWEQIDAQAB";
+ log.info("publicKey = " + publicKey);
+ log.info("privateKey = " + privateKey);
+ String originValue = "{\n"
+ + " \"data\": \"1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说\",\n"
+ + " \"data1\": \"11是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说\",\n"
+ + " \"data2\": \"11是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说1是发送到发送到发哈都是开发计划署的环境封建社会的大是大非上对方哈史蒂芬霍金啊说\"\n"
+ + "}";
+ // 加密前:原数据123456,测试一下
+ System.out.println("加密前:" + originValue);
+
+ String encrypt = encrypt(originValue, publicKey);
+ log.info("加密后:" + encrypt);
+ encrypt = "HxlTvNpgMBj3tcqcXWQc1SFvn8c4nv0HYBlvuVy07BUa1ynQXhhNoT9o7iX5/edJ9xLGITsWRMUfQvuND9ttsyQS44oRmbkRBAdLZQOxaS4hkMDGleWMZII0VgQbULeOd7SEq5Ba2UU7TDySg0VLRgRRgnxt2qUig9Gx/3quQY19Ts9Wu+NBRi1JvCQD5bKi+9BW+jMKIjZ5hx2MwsVVIQDkoQKLcRTV6lejrXdPP7fFo6rG3/OULM7+U/rw2EoC+F+2NRIoEIQj2jclmM8kqAspNJH2NmP/0lsgjay7/nK2nU1Gz/zeQsSVgJmggTUxTImKk5eoqckuNdPD2mwyRQ==";
+ String decrypt = decrypt(encrypt, privateKey);
+ log.info("解密后:" + decrypt);
+ }
+}
+
+
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java
new file mode 100644
index 0000000..700c7f4
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/security/StringUtils.java
@@ -0,0 +1,53 @@
+package com.sf.vertx.security;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+public class StringUtils {
+ /**
+ * 编码方法
+ */
+ private static final Charset CHARSET = StandardCharsets.UTF_8;
+
+ /**
+ * 根据本类的常量对字符串进行获取字节数组的操作
+ *
+ * @param s String:待处理的字符串
+ * @return byte[] 形成的字节数组
+ */
+ public static byte[] getBytes(String s) {
+ return s.getBytes(CHARSET);
+ }
+
+ /**
+ * 将字节数组转为文本
+ *
+ * @param data 字节数组
+ * @return 文本
+ */
+ public static String bytes2String(byte[] data) {
+ return new String(data, CHARSET);
+ }
+
+
+ /**
+ * 字符串是不是空的
+ *
+ * @param str 字符串
+ * @return 结果
+ */
+ public static boolean isBlank(String str) {
+ int strLen;
+ if (str != null && (strLen = str.length()) != 0) {
+ for (int i = 0; i < strLen; ++i) {
+ if (!Character.isWhitespace(str.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java
new file mode 100644
index 0000000..d110daa
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/AppConfigService.java
@@ -0,0 +1,13 @@
+package com.sf.vertx.service;
+
+import com.sf.vertx.api.pojo.AppConfig;
+import com.sf.vertx.api.pojo.VertxConfig;
+
+public interface AppConfigService {
+
+ void saveAppConfig(AppConfig appConfig);
+
+ void deleteAppConfig(AppConfig appConfig);
+
+ void saveVertxConfig(VertxConfig vertxConfig);
+}
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java
new file mode 100644
index 0000000..a784756
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/service/impl/AppConfigServiceImpl.java
@@ -0,0 +1,82 @@
+/*
+package com.sf.vertx.service.impl;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.sf.vertx.api.pojo.AppConfig;
+import com.sf.vertx.api.pojo.VertxConfig;
+import com.sf.vertx.constans.RedisKeyConfig;
+import com.sf.vertx.handle.AppConfigHandler;
+import com.sf.vertx.pojo.ClusterEventMsg;
+import com.sf.vertx.service.AppConfigService;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+public class AppConfigServiceImpl implements AppConfigService {
+ @Value("${server.vertx.environment}")
+ private String vertxEnvironment;
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ */
+/***
+ * 新增、修改
+ *
+ * @param appConfig
+ *//*
+
+ public void saveAppConfig(AppConfig appConfig) {
+ redisTemplate.opsForZSet().add(RedisKeyConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode(), 0);
+ String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode();
+ redisTemplate.opsForValue().set(appCodeKey, JSONObject.toJSONString(appConfig));
+
+ // 发布集群消息
+ ClusterEventMsg msg = new ClusterEventMsg();
+ msg.setType(1);
+ msg.setOperation(1);
+ msg.setAppCode(appConfig.getAppCode());
+ AppConfigHandler.publishClusterEventMsg(msg);
+ }
+
+ */
+/***
+ * 删除
+ *
+ * @param appConfig
+ *//*
+
+ public void deleteAppConfig(AppConfig appConfig) {
+ redisTemplate.opsForZSet().remove(RedisKeyConfig.APP_CONFIG_SET_KEY, appConfig.getAppCode());
+ String appCodeKey = RedisKeyConfig.APP_CONFIG_PREFIX_KEY + ":" + appConfig.getAppCode();
+ redisTemplate.delete(appCodeKey);
+
+ // 发送集群消息
+ ClusterEventMsg msg = new ClusterEventMsg();
+ msg.setType(1);
+ msg.setOperation(3);
+ msg.setAppCode(appConfig.getAppCode());
+ AppConfigHandler.publishClusterEventMsg(msg);
+ }
+
+ */
+/***
+ * 新增、修改
+ *
+ * @param appConfig
+ *//*
+
+ public void saveVertxConfig(VertxConfig vertxConfig) {
+ String vertxConfigKey = RedisKeyConfig.VERTX_CONFIG_STRING_KEY;
+ redisTemplate.opsForValue().set(vertxConfigKey, JSONObject.toJSONString(vertxConfig));
+ AppConfigHandler.initVertxConfig();
+ }
+
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java
new file mode 100644
index 0000000..a0c6728
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/AppUtils.java
@@ -0,0 +1,269 @@
+/*
+package com.sf.vertx.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.NumberUtil;
+import com.sf.vertx.api.pojo.*;
+import com.sf.vertx.enums.GatewayError;
+import com.sf.vertx.enums.GatewayServiceType;
+import com.sf.vertx.enums.MatchType;
+import com.sf.vertx.enums.RequestMethod;
+import com.sf.vertx.handle.AppConfigHandler;
+import io.vertx.circuitbreaker.CircuitBreaker;
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.json.JsonObject;
+import io.vertx.ext.web.RoutingContext;
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import static com.sf.vertx.constans.SACConstants.*;
+
+*/
+/**
+ * 功能描述:
+ *
+ * @author a_kun
+ * @date 2024/5/24 12:30
+ *//*
+
+@Slf4j
+@UtilityClass
+public class AppUtils {
+
+ */
+/**
+ * 将AppConfig设置到上下文中
+ * 配合getAppConfigFromRoutingContext(RoutingContext rc)使用
+ * @param appConfig
+ * @param rc
+ *//*
+
+ public static void setAppConfigToRoutingContext(AppConfig appConfig, RoutingContext rc) {
+ rc.put(APP_CONFIG,appConfig);
+ }
+
+ */
+/**
+ * 从路由上下文获取AppConfig
+ * @param rc
+ * @return AppConfig
+ *//*
+
+ public static AppConfig getAppConfigFromRoutingContext(RoutingContext rc) {
+ return rc.get(APP_CONFIG);
+ }
+
+ */
+/**
+ * 将AppConfig设置到上下文中
+ * 配合getApiConfigFromRoutingContext(RoutingContext rc)使用
+ * @param apiConfig
+ * @param rc
+ *//*
+
+ public static void setApiConfigIntoRoutingContext(ApiConfig apiConfig, RoutingContext rc) {
+ rc.put(API_CONFIG,apiConfig);
+ }
+
+ */
+/**
+ * 从路由上下文获取ApiConfig
+ * @param rc
+ * @return ApiConfig
+ *//*
+
+ public static ApiConfig getApiConfigFromRoutingContext(RoutingContext rc) {
+ return rc.get(API_CONFIG);
+ }
+
+ public static boolean isEnableMockApi(ApiConfig apiConfig) {
+ if (apiConfig != null) {
+ return apiConfig.getMockDefaultHttpStatus() != null;
+ }
+ return false;
+ }
+
+ */
+/**
+ * 判断API服务类型是否是SAC规范服务类型
+ *
+ * @param apiServiceType
+ * @return
+ *//*
+
+ public static boolean isSACServiceType(String apiServiceType) {
+ return GatewayServiceType.SAC.getCode().equals(apiServiceType);
+ }
+
+ public static boolean isEnableDataSecurity(AppConfig appConfig) {
+ return appConfig != null && appConfig.getDataSecurity() != null;
+ }
+
+ public static MockResponse mock(RoutingContext rc) {
+ AppConfig appConfig = getAppConfigFromRoutingContext(rc);
+ ApiConfig apiConfig = getApiConfigFromRoutingContext(rc);
+ String apiServiceType = rc.get(API_SERVICE_TYPE);
+ if (AppUtils.isEnableMockApi(apiConfig)) {
+ // 如果是sac服务,query和body参数都从body中取
+ Integer httpStatus = apiConfig.getMockDefaultHttpStatus();
+ String mockResponse = apiConfig.getMockDefaultResponse();
+ List mockExpectations = apiConfig.getMockExpectations();
+ if (mockExpectations != null && !mockExpectations.isEmpty()) {
+ String bodyStr = rc.body().asString();
+ if (AppUtils.isEnableDataSecurity(appConfig)) {
+ bodyStr = AppConfigHandler.bodyDecrypt(bodyStr, appConfig.getAppCode());
+ }
+ JsonObject jsonBody = new JsonObject(bodyStr);
+ for (MockExpectation mockExpectation : mockExpectations) {
+ // 匹配条件需要全部匹配成功
+ boolean matchSuccess = true;
+ for (MockMatchCondition matchCondition : mockExpectation.getMatchConditions()) {
+ if (!matchSuccess) {
+ break;
+ }
+ MatchType matchType = MatchType.getByCode(matchCondition.getMatchType());
+ if (matchType == null) {
+ matchSuccess = false;
+ break;
+ }
+ String parameterPosition = matchCondition.getParameterPosition();
+ String parameterKey = matchCondition.getParameterKey();
+ List parameterValue = matchCondition.getParameterValue();
+ String requestParameterValue = getRequestParameterValue(AppUtils.isSACServiceType(apiServiceType), parameterPosition, parameterKey, rc.request(), jsonBody);
+ if (!MatchType.IS_NULL.equals(matchType) && !MatchType.NOT_NULL.equals(matchType)) {
+ // 需要值而没有设置值,直接匹配失败
+ if (CollectionUtil.isEmpty(parameterValue) || requestParameterValue == null) {
+ matchSuccess = false;
+ break;
+ }
+ }
+
+ switch (matchType) {
+ case EQ:
+ matchSuccess = parameterValue.get(0).equals(requestParameterValue);
+ break;
+ case NOT_EQ:
+ matchSuccess = !parameterValue.get(0).equals(requestParameterValue);
+ break;
+ case GT:
+ if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) {
+ matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) > 0;
+ } else {
+ matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) > 0;
+ }
+ break;
+ case GE:
+ if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) {
+ matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) >= 0;
+ } else {
+ matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) >= 0;
+ }
+ break;
+ case LT:
+ if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) {
+ matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) < 0;
+ } else {
+ matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) < 0;
+ }
+ break;
+ case LE:
+ if (NumberUtil.isNumber(requestParameterValue) && NumberUtil.isNumber(parameterValue.get(0))) {
+ matchSuccess = new BigDecimal(requestParameterValue).compareTo(new BigDecimal(parameterValue.get(0))) <= 0;
+ } else {
+ matchSuccess = requestParameterValue.compareTo(parameterValue.get(0)) <= 0;
+ }
+ break;
+ case IN:
+ matchSuccess = parameterValue.contains(requestParameterValue);
+ break;
+ case NOT_IN:
+ matchSuccess = !parameterValue.contains(requestParameterValue);
+ break;
+ case IS_NULL:
+ matchSuccess = requestParameterValue == null;
+ break;
+ case NOT_NULL:
+ matchSuccess = requestParameterValue != null;
+ break;
+ default:
+ break;
+ }
+
+ }
+ if (matchSuccess) {
+ httpStatus = mockExpectation.getHttpStatus();
+ mockResponse = mockExpectation.getMockResponse();
+ break;
+ }
+ }
+ }
+ return new MockResponse(httpStatus, mockResponse);
+ }
+ return null;
+ }
+
+ private static String getRequestParameterValue(Boolean isServiceTypeSac, String parameterPosition, String parameterKey, HttpServerRequest request, JsonObject jsonBody) {
+ switch (parameterPosition) {
+ case "query":
+ if (isServiceTypeSac) {
+ return jsonBody.getString(parameterKey);
+ }
+ return request.getParam(parameterKey);
+ case "header":
+ return request.getHeader(parameterKey);
+ case "body":
+ return jsonBody.getString(parameterKey);
+ default:
+ break;
+ }
+ return null;
+ }
+
+ public static String getResponseMsgByGatewayError(GatewayError gatewayError) {
+ if (gatewayError != null) {
+ return gatewayError.getReasonPhrase();
+ }
+ return getResponseMsgByGatewayError(GatewayError.DEFAULT_SERVICE_ERROR);
+ }
+
+ public static JsonObject getResponseJsonByGatewayError(GatewayError gatewayError) {
+ if (gatewayError != null) {
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.put("code",gatewayError.getCode());
+ jsonObject.put("msg",gatewayError.getReasonPhrase());
+ return jsonObject;
+ }
+ return getResponseJsonByGatewayError(GatewayError.DEFAULT_SERVICE_ERROR);
+ }
+
+ public static boolean isAnalysisBody(AppConfig appConfig, ApiConfig apiConfig,String apiServiceType) {
+ // 不是Mock(是mock模式的话,存在body就解析。)
+ if (AppUtils.isEnableMockApi(apiConfig)) {
+ return true;
+ }
+ // SAC模式,实际API请求方式GET,DELETE,HEAD请求需要解析body,可能需要二次处理。(SAC模式请求方式参数统一使用body传递)
+ // SAC模式,如果是POST或者PUT,并且没有设置数据加密则跳过
+ if (AppUtils.isSACServiceType(apiServiceType)) {
+ RequestMethod requestMethod = RequestMethod.getByCode(apiConfig.getMethod());
+ if (RequestMethod.GET.equals(requestMethod)
+ || RequestMethod.DELETE.equals(requestMethod)
+ || RequestMethod.HEAD.equals(requestMethod)) {
+ return true;
+ }
+ String keyCircuitBreaker = appConfig.getAppCode() + ":" + apiConfig.getApiCode() + ":" + "CIRCUIT_BREAKER";
+ CircuitBreaker circuitBreaker = AppConfigHandler.getApiCodeCircuitBreaker(keyCircuitBreaker);
+ boolean isDataSecurity = AppUtils.isEnableDataSecurity(appConfig);
+ // 文件上传不走加解密
+ return (isDataSecurity || circuitBreaker != null);
+ }else {
+ // 未启用Mock,OPEN模式都不会解析body
+ return false;
+ }
+
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java
new file mode 100644
index 0000000..638c1da
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/Filter.java
@@ -0,0 +1,91 @@
+package com.sf.vertx.utils;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import io.vertx.core.Handler;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.streams.ReadStream;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class Filter implements ReadStream {
+
+ private final AtomicBoolean paused = new AtomicBoolean();
+ private ReadStream stream;
+ private Buffer expected = Buffer.buffer();
+ private Handler dataHandler;
+ private Handler exceptionHandler;
+ private Handler endHandler;
+
+ /***
+ *
+ * @param s
+ * @param encryption true: 请求参数解密, false: 返回数据 加密
+ * @return
+ */
+ public ReadStream init(String uri, ReadStream s, Boolean encryption) {
+ stream = s;
+ stream.handler(buff -> {
+ if (dataHandler != null) {
+ byte[] bytes = new byte[buff.length()];
+ for (int i = 0; i < bytes.length; i++) {
+ // bytes[i] = (byte) (('a' - 'A') + buff.getByte(i));
+ bytes[i] = (byte) (buff.getByte(i));
+ }
+
+ String res = new String(bytes);
+ log.info("request uri:{}, return data:{}", uri, res);
+ expected.appendBytes(bytes);
+ dataHandler.handle(Buffer.buffer(bytes));
+ }
+ });
+ stream.exceptionHandler(err -> {
+ if (exceptionHandler != null) {
+ exceptionHandler.handle(err);
+ }
+ });
+ stream.endHandler(v -> {
+ if (endHandler != null) {
+ endHandler.handle(v);
+ }
+ });
+ return this;
+ }
+
+ @Override
+ public ReadStream pause() {
+ paused.set(true);
+ stream.pause();
+ return this;
+ }
+
+ @Override
+ public ReadStream resume() {
+ stream.resume();
+ return this;
+ }
+
+ @Override
+ public ReadStream fetch(long amount) {
+ stream.fetch(amount);
+ return this;
+ }
+
+ @Override
+ public ReadStream exceptionHandler(Handler handler) {
+ exceptionHandler = handler;
+ return this;
+ }
+
+ @Override
+ public ReadStream handler(Handler handler) {
+ dataHandler = handler;
+ return this;
+ }
+
+ @Override
+ public ReadStream endHandler(Handler handler) {
+ endHandler = handler;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java
new file mode 100644
index 0000000..770511b
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/java/com/sf/vertx/utils/ProxyTool.java
@@ -0,0 +1,82 @@
+/*
+package com.sf.vertx.utils;
+
+import java.util.List;
+import java.util.regex.Pattern;
+
+import com.sf.vertx.api.pojo.Node;
+import com.sf.vertx.api.pojo.RouteContent;
+import com.sf.vertx.arithmetic.roundRobin.SacLoadBalancing;
+import com.sf.vertx.arithmetic.roundRobin.WeightedRoundRobin;
+import com.sf.vertx.handle.AppConfigHandler;
+
+import io.vertx.core.http.HttpServerRequest;
+import io.vertx.core.net.SocketAddress;
+import io.vertx.ext.web.handler.HttpException;
+import lombok.extern.slf4j.Slf4j;
+
+*/
+/***
+ * 反向代理工具类
+ *
+ * @author xy
+ *
+ *//*
+
+@Slf4j
+public class ProxyTool {
+
+ public static SocketAddress resolveOriginAddress(HttpServerRequest request) {
+ String appCode = request.getHeader(AppConfigHandler.getAppCodeHeaderKey());
+ String apiCode = request.getHeader(AppConfigHandler.getApiCodeHeaderKey());
+ log.info("uri:{}, header appCode:{},apiCode:{}", request.uri(), appCode, apiCode);
+ // 判断 "routeType": "WEIGHT_ROUTE", // 路由类型 WEIGHT_ROUTE ,HEADER_ROUTE
+ String key = appCode + ":" + apiCode;
+ Integer routerType = AppConfigHandler.routerType(key);
+ SocketAddress socketAddress = null;
+ switch (routerType) {
+ case 1:
+ SacLoadBalancing sacLoadBalancing = AppConfigHandler.getLoadBalancing(key);
+ Node node = sacLoadBalancing.selectNode();
+ socketAddress = SocketAddress.inetSocketAddress(node.getPort(), node.getIp());
+ log.info("sacLoadBalancing address:{},port:{}", socketAddress.host(), socketAddress.port());
+ return socketAddress;
+ case 2:
+ List routeContentList = AppConfigHandler.routerHeaderConentList(key);
+ if(routeContentList != null && routeContentList.size() > 0) {
+ for (RouteContent routeContent : routeContentList) {
+ String headerValue = request.getHeader(routeContent.getHeaderKey());
+ List headerValues = routeContent.getHeaderValues();
+ // String matchType = routeContent.getMatchType();
+ if(headerValues.contains(headerValue)) {
+ socketAddress = SocketAddress.inetSocketAddress(routeContent.getServerAddress().getPort(), routeContent.getServerAddress().getHost());
+ log.info("sacLoadBalancing address:{},port:{}", socketAddress.host(), socketAddress.port());
+ return socketAddress;
+ }
+ }
+ }
+ break;
+ }
+
+ // 抛出异常,无法找到负载均衡node节点
+ throw new HttpException(10021);
+ }
+
+ public static boolean regexMatch(String pattern, String target) {
+ return Pattern.matches(pattern, target);
+ }
+
+ public static SacLoadBalancing roundRobin(List nodeList) {
+ WeightedRoundRobin weightedRoundRobin = new WeightedRoundRobin();
+ for (Node node : nodeList) {
+ int weight = node.getWeight() != null ? node.getWeight() : 1;
+ node.setWeight(weight);
+ node.setCurrentWeight(weight);
+ node.setEffectiveWeight(weight);
+ weightedRoundRobin.totalWeight += node.getEffectiveWeight();
+ }
+ weightedRoundRobin.init(nodeList);
+ return weightedRoundRobin;
+ }
+}
+*/
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..1beae65
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Automatic-Module-Name: io.vertx.core
+
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json
new file mode 100644
index 0000000..a489713
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/native-image/io.vertx/vertx-core/reflect-config.json
@@ -0,0 +1,38 @@
+[
+ {
+ "name": "java.lang.Thread$Builder",
+ "condition": {
+ "typeReachable": "io.vertx.core.impl.VertxImpl"
+ },
+ "methods": [
+ {
+ "name": "factory",
+ "parameterTypes": []
+ }
+ ]
+ },
+ {
+ "name": "java.lang.Thread$Builder$OfVirtual",
+ "condition": {
+ "typeReachable": "io.vertx.core.impl.VertxImpl"
+ },
+ "methods": [
+ {
+ "name": "name",
+ "parameterTypes": ["java.lang.String", "long"]
+ }
+ ]
+ },
+ {
+ "name": "java.lang.Thread",
+ "condition": {
+ "typeReachable": "io.vertx.core.impl.VertxImpl"
+ },
+ "methods": [
+ {
+ "name": "ofVirtual",
+ "parameterTypes": []
+ }
+ ]
+ }
+]
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory
new file mode 100644
index 0000000..d6e4603
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/services/io.vertx.core.spi.launcher.CommandFactory
@@ -0,0 +1,18 @@
+# Copyright (c) 2011-2017 Contributors to the Eclipse Foundation
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License 2.0 which is available at
+# http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+# which is available at https://www.apache.org/licenses/LICENSE-2.0.
+#
+# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+
+# Core commands
+io.vertx.core.impl.launcher.commands.RunCommandFactory
+io.vertx.core.impl.launcher.commands.VersionCommandFactory
+io.vertx.core.impl.launcher.commands.BareCommandFactory
+
+# Background application control
+io.vertx.core.impl.launcher.commands.ListCommandFactory
+io.vertx.core.impl.launcher.commands.StartCommandFactory
+io.vertx.core.impl.launcher.commands.StopCommandFactory
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt
new file mode 100644
index 0000000..f2ab45c
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/META-INF/vertx/vertx-version.txt
@@ -0,0 +1 @@
+${project.version}
\ No newline at end of file
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/application.yaml b/zt-vertx/zt-vertx-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..34332be
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/application.yaml
@@ -0,0 +1,39 @@
+# 开发环境配置
+
+vertx:
+ deploymentMode: 1 # 1:单机 2:集群
+ isSSL: false # vertx https或者http启动, ssl 证书有过期时间
+ rpcPort: 80 # RPC调用使用的端口
+ rpcUri: /rpc.sac # RPC调用使用的url
+ sacResponseHeaderKey: sacErrorCode
+ environment: dev
+ configPort: 5566 # 注册配置的端口
+ blackIP:
+ - 192.168.1.20
+ - 192.168.1.21
+ cluster:
+ ip: 127.0.0.1
+ clusterName: sac-dev
+ networkPort: 5701
+ portAutoIncrement: false
+
+# 日志配置
+logging:
+ level:
+ com.sf: info
+ io.vertx: debug
+ org.springframework: warn
+
+
+# redis 配置
+redis:
+ clientType: STANDALONE
+ poolName: vertx-redis
+ maxPoolSize: 8
+ maxPoolWaiting: 32
+ poolCleanerInterval: 30000
+ urls:
+ - redis://192.168.1.23:22002/1
+ # 密码
+ password:
+
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/cacerts.jks b/zt-vertx/zt-vertx-service/src/main/resources/cacerts.jks
new file mode 100644
index 0000000..bd8a403
Binary files /dev/null and b/zt-vertx/zt-vertx-service/src/main/resources/cacerts.jks differ
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/cluster.xml b/zt-vertx/zt-vertx-service/src/main/resources/cluster.xml
new file mode 100644
index 0000000..e521a5a
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/cluster.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ 5701
+
+
+
+
+ 127.0.0.1
+
+ 127.0.0.1
+
+
+
+
+
+
+
+
+
+ http://127.0.0.1:8080/mancenter
+
+
+
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties b/zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties
new file mode 100644
index 0000000..93de005
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/i18n/messages.properties
@@ -0,0 +1,38 @@
+#错误消息
+not.null=* 必须填写
+user.jcaptcha.error=验证码错误
+user.jcaptcha.expire=验证码已失效
+user.not.exists=用户不存在/密码错误
+user.password.not.match=用户不存在/密码错误
+user.password.retry.limit.count=密码输入错误{0}次
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
+user.password.delete=对不起,您的账号已被删除
+user.blocked=用户已封禁,请联系管理员
+role.blocked=角色已封禁,请联系管理员
+login.blocked=很遗憾,访问IP已被列入系统黑名单
+user.logout.success=退出成功
+
+length.not.valid=长度必须在{min}到{max}个字符之间
+
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
+user.password.not.valid=* 5-50个字符
+
+user.email.not.valid=邮箱格式错误
+user.mobile.phone.number.not.valid=手机号格式错误
+user.login.success=登录成功
+user.register.success=注册成功
+user.notfound=请重新登录
+user.forcelogout=管理员强制退出,请重新登录
+user.unknown.error=未知错误,请重新登录
+
+##文件上传消息
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小!
允许的文件最大大小是:{0}MB!
+upload.filename.exceed.length=上传的文件名最长{0}个字符
+
+##权限
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/keystore.jks b/zt-vertx/zt-vertx-service/src/main/resources/keystore.jks
new file mode 100644
index 0000000..4da3ff6
Binary files /dev/null and b/zt-vertx/zt-vertx-service/src/main/resources/keystore.jks differ
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml b/zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..f475925
--- /dev/null
+++ b/zt-vertx/zt-vertx-service/src/main/resources/log4j2.xml
@@ -0,0 +1,123 @@
+
+
+
+
+ ./vertx-logs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %d %p %C{} [%t] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %d %p %C{} [%t] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %d %p %C{} [%t] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %d %p %C{} [%t] %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/zt-vertx/zt-vertx-service/src/main/resources/server.cer b/zt-vertx/zt-vertx-service/src/main/resources/server.cer
new file mode 100644
index 0000000..b9b8a46
Binary files /dev/null and b/zt-vertx/zt-vertx-service/src/main/resources/server.cer differ