feat: 实现IP白名单严格拦截、三方Webhook多租户事件订阅与MCP专线业务调用
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package com.iot.gateway.common;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 网关与核心业务后端 (IOT-backend) 的专线 HTTP 桥接客户端
|
||||
* <p>
|
||||
* 自动向核心后端传递 X-Internal-Secret 与 X-Tenant-Id,
|
||||
* 触发核心后端切入指定租户的独立物理数据库。
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BackendInternalClient {
|
||||
|
||||
@Value("${gateway.backend.base-url:http://localhost:8080}")
|
||||
private String backendBaseUrl;
|
||||
|
||||
@Value("${gateway.backend.internal-secret:iot-internal-open-gateway-secret-key-20261009-xyz}")
|
||||
private String internalSecret;
|
||||
|
||||
private final HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(5))
|
||||
.build();
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* 内部专线 GET 请求
|
||||
*/
|
||||
public JsonNode get(String path, Long tenantId, Map<String, String> queryParams) {
|
||||
StringBuilder urlBuilder = new StringBuilder(backendBaseUrl).append(path);
|
||||
if (queryParams != null && !queryParams.isEmpty()) {
|
||||
urlBuilder.append("?");
|
||||
queryParams.forEach((k, v) -> {
|
||||
if (v != null && !v.isEmpty()) {
|
||||
urlBuilder.append(k).append("=").append(v).append("&");
|
||||
}
|
||||
});
|
||||
urlBuilder.deleteCharAt(urlBuilder.length() - 1);
|
||||
}
|
||||
|
||||
try {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(urlBuilder.toString()))
|
||||
.header("X-Internal-Secret", internalSecret)
|
||||
.header("X-Tenant-Id", String.valueOf(tenantId != null ? tenantId : 1L))
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(Duration.ofSeconds(6))
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
HttpResponse<String> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (resp.statusCode() == 200) {
|
||||
return objectMapper.readTree(resp.body());
|
||||
} else {
|
||||
log.warn("核心后端内部专线响应非 200: status={}, body={}", resp.statusCode(), resp.body());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("调用核心后端内部接口异常: url={}, tenantId={}, err={}", urlBuilder, tenantId, e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部专线 POST 请求
|
||||
*/
|
||||
public JsonNode post(String path, Long tenantId, Map<String, Object> body) {
|
||||
String url = backendBaseUrl + path;
|
||||
try {
|
||||
String jsonStr = objectMapper.writeValueAsString(body != null ? body : new HashMap<>());
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.header("X-Internal-Secret", internalSecret)
|
||||
.header("X-Tenant-Id", String.valueOf(tenantId != null ? tenantId : 1L))
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(Duration.ofSeconds(6))
|
||||
.POST(HttpRequest.BodyPublishers.ofString(jsonStr))
|
||||
.build();
|
||||
|
||||
HttpResponse<String> resp = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (resp.statusCode() == 200) {
|
||||
return objectMapper.readTree(resp.body());
|
||||
} else {
|
||||
log.warn("核心后端内部专线响应非 200: status={}, body={}", resp.statusCode(), resp.body());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("调用核心后端内部 POST 异常: url={}, tenantId={}, err={}", url, tenantId, e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user