去除代理头支持, 增加节点面板通信加密 增加导出sql

This commit is contained in:
qaq
2025-07-11 16:24:12 +08:00
parent b690e68951
commit e313a1953c
21 changed files with 960 additions and 143 deletions
@@ -26,9 +26,5 @@ public class ForwardDto {
@Min(value = 1, message = "端口号不能小于1")
@Max(value = 65535, message = "端口号不能大于65535")
private Integer inPort;
/**
* 是否启用代理协议(0: 禁用, 1: 启用)
*/
private Integer proxyProtocol = 0; // 设置默认值为0(禁用)
}
@@ -32,9 +32,4 @@ public class ForwardUpdateDto {
@Min(value = 1, message = "端口号不能小于1")
@Max(value = 65535, message = "端口号不能大于65535")
private Integer inPort;
/**
* 是否启用代理协议(0: 禁用, 1: 启用)
*/
private Integer proxyProtocol = 0; // 设置默认值为0(禁用)
}
@@ -85,9 +85,4 @@ public class ForwardWithTunnelDto {
private Long outFlow;
private String strategy;
/**
* 是否启用代理协议(0: 禁用, 1: 启用)
*/
private Integer proxyProtocol;
}
@@ -0,0 +1,168 @@
package com.admin.common.utils;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Base64;
/**
* AES加密工具类
* 使用AES-256-GCM模式,与Go端保持兼容
*/
@Slf4j
public class AESCrypto {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final int GCM_IV_LENGTH = 12; // GCM推荐的IV长度
private static final int GCM_TAG_LENGTH = 16; // GCM认证标签长度
private final SecretKeySpec secretKey;
private final SecureRandom secureRandom;
/**
* 构造函数
* @param secret 密钥字符串,将使用SHA-256转换为32字节密钥
*/
public AESCrypto(String secret) {
if (secret == null || secret.isEmpty()) {
throw new IllegalArgumentException("密钥不能为空");
}
try {
// 使用SHA-256将密码转换为32字节密钥,与Go端保持一致
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] keyBytes = sha256.digest(secret.getBytes(StandardCharsets.UTF_8));
this.secretKey = new SecretKeySpec(keyBytes, ALGORITHM);
this.secureRandom = new SecureRandom();
log.debug("AES加密器初始化成功");
} catch (Exception e) {
log.error("AES加密器初始化失败", e);
throw new RuntimeException("AES加密器初始化失败: " + e.getMessage(), e);
}
}
/**
* 加密数据
* @param data 要加密的原始数据
* @return Base64编码的加密数据,格式为: nonce + ciphertext
*/
public String encrypt(byte[] data) {
if (data == null || data.length == 0) {
throw new IllegalArgumentException("待加密数据不能为空");
}
try {
// 生成随机IV(nonce)
byte[] iv = new byte[GCM_IV_LENGTH];
secureRandom.nextBytes(iv);
// 创建GCM参数规范
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
// 初始化Cipher
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmSpec);
// 加密数据
byte[] ciphertext = cipher.doFinal(data);
// 组合IV + ciphertext,与Go端格式保持一致
ByteBuffer buffer = ByteBuffer.allocate(iv.length + ciphertext.length);
buffer.put(iv);
buffer.put(ciphertext);
// 返回Base64编码结果
return Base64.getEncoder().encodeToString(buffer.array());
} catch (Exception e) {
log.error("数据加密失败", e);
throw new RuntimeException("数据加密失败: " + e.getMessage(), e);
}
}
/**
* 加密字符串
* @param data 要加密的字符串
* @return Base64编码的加密数据
*/
public String encrypt(String data) {
if (data == null) {
throw new IllegalArgumentException("待加密字符串不能为空");
}
return encrypt(data.getBytes(StandardCharsets.UTF_8));
}
/**
* 解密数据
* @param encryptedData Base64编码的加密数据
* @return 解密后的原始数据
*/
public byte[] decrypt(String encryptedData) {
if (encryptedData == null || encryptedData.isEmpty()) {
throw new IllegalArgumentException("加密数据不能为空");
}
try {
// Base64解码
byte[] encrypted = Base64.getDecoder().decode(encryptedData);
if (encrypted.length < GCM_IV_LENGTH) {
throw new IllegalArgumentException("加密数据长度不足");
}
// 分离IV和密文
ByteBuffer buffer = ByteBuffer.wrap(encrypted);
byte[] iv = new byte[GCM_IV_LENGTH];
buffer.get(iv);
byte[] ciphertext = new byte[buffer.remaining()];
buffer.get(ciphertext);
// 创建GCM参数规范
GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
// 初始化Cipher
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmSpec);
// 解密数据
return cipher.doFinal(ciphertext);
} catch (Exception e) {
log.error("数据解密失败", e);
throw new RuntimeException("数据解密失败: " + e.getMessage(), e);
}
}
/**
* 解密字符串
* @param encryptedData Base64编码的加密数据
* @return 解密后的字符串
*/
public String decryptString(String encryptedData) {
byte[] decrypted = decrypt(encryptedData);
return new String(decrypted, StandardCharsets.UTF_8);
}
/**
* 创建AES加密器实例
* @param secret 密钥字符串
* @return AES加密器实例,如果创建失败返回null
*/
public static AESCrypto create(String secret) {
try {
return new AESCrypto(secret);
} catch (Exception e) {
log.error("创建AES加密器失败: {}", e.getMessage());
return null;
}
}
}
@@ -31,21 +31,21 @@ public class GostUtil {
return WebSocketServer.send_msg(node_id, req, "DeleteLimiters");
}
public static GostDto AddService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy, Integer proxy_protocol) {
public static GostDto AddService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy) {
JSONArray services = new JSONArray();
String[] protocols = {"tcp", "udp"};
for (String protocol : protocols) {
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy, proxy_protocol);
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy);
services.add(service);
}
return WebSocketServer.send_msg(node_id, services, "AddService");
}
public static GostDto UpdateService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy, Integer proxy_protocol) {
public static GostDto UpdateService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy) {
JSONArray services = new JSONArray();
String[] protocols = {"tcp", "udp"};
for (String protocol : protocols) {
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy, proxy_protocol);
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy);
services.add(service);
}
return WebSocketServer.send_msg(node_id, services, "UpdateService");
@@ -255,7 +255,7 @@ public class GostUtil {
return data;
}
private static JSONObject createServiceConfig(String name, Integer in_port, Integer limiter, String remoteAddr, String protocol, Integer fow_type, Tunnel tunnel, String strategy, Integer proxy_protocol) {
private static JSONObject createServiceConfig(String name, Integer in_port, Integer limiter, String remoteAddr, String protocol, Integer fow_type, Tunnel tunnel, String strategy) {
JSONObject service = new JSONObject();
service.put("name", name + "_" + protocol);
if (Objects.equals(protocol, "tcp")){
@@ -279,13 +279,9 @@ public class GostUtil {
// 端口转发需要配置转发器
if (isPortForwarding(fow_type)) {
JSONObject forwarder = createForwarder(protocol, remoteAddr, strategy);
JSONObject forwarder = createForwarder(remoteAddr, strategy);
service.put("forwarder", forwarder);
}
JSONObject metadata = new JSONObject();
metadata.put("proxyProtocol", proxy_protocol);
service.put("metadata", metadata);
return service;
}
@@ -312,7 +308,7 @@ public class GostUtil {
return listener;
}
private static JSONObject createForwarder(String protocol, String remoteAddr, String strategy) {
private static JSONObject createForwarder(String remoteAddr, String strategy) {
JSONObject forwarder = new JSONObject();
JSONArray nodes = new JSONArray();
@@ -6,6 +6,7 @@ import com.admin.common.dto.GostDto;
import com.admin.common.task.CheckGostConfigAsync;
import com.admin.entity.Node;
import com.admin.service.NodeService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -42,6 +43,25 @@ public class WebSocketServer extends TextWebSocketHandler {
// 存储等待响应的请求,key为requestId,value为CompletableFuture
private static final ConcurrentHashMap<String, CompletableFuture<GostDto>> pendingRequests = new ConcurrentHashMap<>();
// 缓存加密器实例,避免重复创建
private static final ConcurrentHashMap<String, AESCrypto> cryptoCache = new ConcurrentHashMap<>();
/**
* 加密消息包装器
*/
public static class EncryptedMessage {
private boolean encrypted;
private String data;
private Long timestamp;
// getters and setters
public boolean isEncrypted() { return encrypted; }
public void setEncrypted(boolean encrypted) { this.encrypted = encrypted; }
public String getData() { return data; }
public void setData(String data) { this.data = data; }
public Long getTimestamp() { return timestamp; }
public void setTimestamp(Long timestamp) { this.timestamp = timestamp; }
}
//接受客户端消息
@Override
@@ -51,15 +71,19 @@ public class WebSocketServer extends TextWebSocketHandler {
String id = session.getAttributes().get("id").toString();
String type = session.getAttributes().get("type").toString();
String nodeSecret = (String) session.getAttributes().get("nodeSecret");
// 尝试解密消息
String decryptedPayload = decryptMessageIfNeeded(message.getPayload(), nodeSecret);
if (message.getPayload().contains("memory_usage")){
if (decryptedPayload.contains("memory_usage")){
// 先发送确认消息
sendToUser(session, "{\"type\":\"call\"}");
}else if (message.getPayload().contains("requestId")) {
log.info("收到消息: {}", message.getPayload());
sendToUser(session, "{\"type\":\"call\"}", nodeSecret);
}else if (decryptedPayload.contains("requestId")) {
log.info("收到消息: {}", decryptedPayload);
// 处理命令响应消息
try {
JSONObject responseJson = JSONObject.parseObject(message.getPayload());
JSONObject responseJson = JSONObject.parseObject(decryptedPayload);
String requestId = responseJson.getString("requestId");
String responseMessage = responseJson.getString("message");
String responseType = responseJson.getString("type");
@@ -91,7 +115,7 @@ public class WebSocketServer extends TextWebSocketHandler {
log.error("处理响应消息失败: {}", e.getMessage(), e);
}
} else {
log.info("收到消息: {}", message.getPayload());
log.info("收到消息: {}", decryptedPayload);
}
// 如果是节点类型,转发消息给其他会话
@@ -99,13 +123,13 @@ public class WebSocketServer extends TextWebSocketHandler {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", id);
jsonObject.put("type", "info");
jsonObject.put("data", message.getPayload());
jsonObject.put("data", decryptedPayload);
String broadcastMessage = jsonObject.toJSONString();
// 异步处理广播消息,避免阻塞当前线程
for (WebSocketSession targetSession : activeSessions) {
if (targetSession != null && targetSession.isOpen() && !targetSession.equals(session)) {
sendToUser(targetSession, broadcastMessage);
sendToUser(targetSession, broadcastMessage, null);
}
}
}
@@ -115,6 +139,78 @@ public class WebSocketServer extends TextWebSocketHandler {
}
}
/**
* 尝试解密消息(如果需要)
*/
private String decryptMessageIfNeeded(String payload, String nodeSecret) {
if (payload == null || payload.trim().isEmpty()) {
return payload;
}
try {
// 尝试解析为加密消息格式
EncryptedMessage encryptedMessage = JSON.parseObject(payload, EncryptedMessage.class);
if (encryptedMessage.isEncrypted() && encryptedMessage.getData() != null) {
// 获取或创建加密器
AESCrypto crypto = getOrCreateCrypto(nodeSecret);
if (crypto == null) {
log.warn("⚠️ 收到加密消息但无法创建解密器,使用原始数据");
return payload;
}
// 解密数据
String decryptedData = crypto.decryptString(encryptedMessage.getData());
log.debug("🔓 WebSocket消息解密成功");
return decryptedData;
}
} catch (Exception e) {
// 解析失败,可能是非加密格式,直接返回原始数据
log.debug("WebSocket消息未加密或解密失败,使用原始数据: {}", e.getMessage());
}
return payload;
}
/**
* 加密消息(如果可能)
*/
private static String encryptMessageIfPossible(String message, String nodeSecret) {
if (message == null || nodeSecret == null) {
return message;
}
try {
AESCrypto crypto = getOrCreateCrypto(nodeSecret);
if (crypto != null) {
String encryptedData = crypto.encrypt(message);
// 创建加密消息包装器
JSONObject encryptedMessage = new JSONObject();
encryptedMessage.put("encrypted", true);
encryptedMessage.put("data", encryptedData);
encryptedMessage.put("timestamp", System.currentTimeMillis());
log.debug("🔐 WebSocket消息加密成功");
return encryptedMessage.toJSONString();
}
} catch (Exception e) {
log.warn("⚠️ WebSocket消息加密失败,发送原始数据: {}", e.getMessage());
}
return message;
}
/**
* 获取或创建加密器实例
*/
private static AESCrypto getOrCreateCrypto(String secret) {
if (secret == null || secret.isEmpty()) {
return null;
}
return cryptoCache.computeIfAbsent(secret, AESCrypto::create);
}
// 建立连接
@Override
public void afterConnectionEstablished(WebSocketSession session) {
@@ -232,7 +328,7 @@ public class WebSocketServer extends TextWebSocketHandler {
boolean shouldUpdateOffline = true;
try {
// 尝试发送验证消息,如果发送成功说明连接可能还活跃
sendToUser(session, "{\"type\":\"call\"}");
sendToUser(session, "{\"type\":\"call\"}", null);
log.warn("节点 {} 连接关闭但仍能发送消息,可能是假断开", nodeId);
shouldUpdateOffline = false;
} catch (Exception e) {
@@ -277,6 +373,12 @@ public class WebSocketServer extends TextWebSocketHandler {
// 点对点发送消息
@SneakyThrows
public static void sendToUser(WebSocketSession socketSession, String message) {
sendToUser(socketSession, message, null);
}
// 点对点发送消息(支持加密)
@SneakyThrows
public static void sendToUser(WebSocketSession socketSession, String message, String nodeSecret) {
if (socketSession != null && socketSession.isOpen()) {
String sessionId = socketSession.getId();
Object lock = sessionLocks.computeIfAbsent(sessionId, k -> new Object());
@@ -284,7 +386,15 @@ public class WebSocketServer extends TextWebSocketHandler {
synchronized (lock) {
try {
if (socketSession.isOpen()) {
socketSession.sendMessage(new TextMessage(message));
// 如果是节点连接且有密钥,尝试加密消息
String finalMessage = message;
if (nodeSecret != null && !nodeSecret.isEmpty()) {
String type = (String) socketSession.getAttributes().get("type");
if ("1".equals(type)) { // 节点连接
finalMessage = encryptMessageIfPossible(message, nodeSecret);
}
}
socketSession.sendMessage(new TextMessage(finalMessage));
}
} catch (Exception e) {
log.error("发送WebSocket消息失败 [sessionId={}]: {}", sessionId, e.getMessage());
@@ -354,13 +464,15 @@ public class WebSocketServer extends TextWebSocketHandler {
CompletableFuture<GostDto> future = new CompletableFuture<>();
pendingRequests.put(requestId, future);
// 获取节点密钥用于加密
String nodeSecret = (String) nodeSession.getAttributes().get("nodeSecret");
try {
JSONObject data = new JSONObject();
data.put("type", type);
data.put("data", msg);
data.put("requestId", requestId);
sendToUser(nodeSession, data.toJSONString());
sendToUser(nodeSession, data.toJSONString(), nodeSecret);
GostDto result = future.get(10, TimeUnit.SECONDS);
log.debug("成功发送消息到节点 {} 并收到响应: {}", node_id, result.getMsg());
@@ -0,0 +1,81 @@
package com.admin.config;
import com.admin.common.utils.AESCrypto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
/**
* 加密配置管理类
* 统一管理AES加密功能,为HTTP上报和WebSocket通信提供加密支持
*/
@Component
@Slf4j
public class EncryptionConfig {
// 缓存加密器实例,避免重复创建
private static final ConcurrentHashMap<String, AESCrypto> CRYPTO_CACHE = new ConcurrentHashMap<>();
/**
* 获取或创建AES加密器实例
*
* @param secret 密钥字符串
* @return AES加密器实例,如果创建失败返回null
*/
public static AESCrypto getOrCreateCrypto(String secret) {
if (secret == null || secret.isEmpty()) {
return null;
}
return CRYPTO_CACHE.computeIfAbsent(secret, AESCrypto::create);
}
/**
* 检测消息是否为加密格式
*
* @param message 消息内容
* @return 如果是加密格式返回true,否则返回false
*/
public static boolean isEncryptedMessage(String message) {
if (message == null || message.trim().isEmpty()) {
return false;
}
try {
// 简单检查是否包含加密标识
return message.contains("\"encrypted\":true") || message.contains("\"encrypted\": true");
} catch (Exception e) {
return false;
}
}
/**
* 清理缓存的加密器实例
*
* @param secret 要清理的密钥
*/
public static void clearCrypto(String secret) {
if (secret != null) {
CRYPTO_CACHE.remove(secret);
log.debug("已清理密钥对应的加密器实例");
}
}
/**
* 清理所有缓存的加密器实例
*/
public static void clearAllCrypto() {
int size = CRYPTO_CACHE.size();
CRYPTO_CACHE.clear();
log.info("已清理所有加密器实例缓存,共清理 {} 个实例", size);
}
/**
* 获取当前缓存的加密器数量
*
* @return 缓存的加密器数量
*/
public static int getCacheSize() {
return CRYPTO_CACHE.size();
}
}
@@ -5,11 +5,15 @@ import com.admin.common.dto.FlowDto;
import com.admin.common.dto.GostConfigDto;
import com.admin.common.lang.R;
import com.admin.common.task.CheckGostConfigAsync;
import com.admin.common.utils.AESCrypto;
import com.admin.common.utils.GostUtil;
import com.admin.entity.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import java.math.BigDecimal;
@@ -41,6 +45,7 @@ import java.util.stream.Collectors;
@RestController
@RequestMapping("/flow")
@CrossOrigin
@Slf4j
public class FlowController extends BaseController {
// 常量定义
@@ -55,19 +60,51 @@ public class FlowController extends BaseController {
private static final ConcurrentHashMap<String, Object> TUNNEL_LOCKS = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Object> FORWARD_LOCKS = new ConcurrentHashMap<>();
// 缓存加密器实例,避免重复创建
private static final ConcurrentHashMap<String, AESCrypto> CRYPTO_CACHE = new ConcurrentHashMap<>();
@Resource
CheckGostConfigAsync checkGostConfigAsync;
@PostMapping("/config")
@LogAnnotation
public String config(@RequestBody GostConfigDto gostConfigDto, String secret) {
Node node = nodeService.getOne(new QueryWrapper<Node>().eq("secret", secret));
if (node == null) return SUCCESS_RESPONSE;
checkGostConfigAsync.cleanNodeConfigs(node.getId().toString(), gostConfigDto);
return SUCCESS_RESPONSE;
/**
* 加密消息包装器
*/
public static class EncryptedMessage {
private boolean encrypted;
private String data;
private Long timestamp;
// getters and setters
public boolean isEncrypted() { return encrypted; }
public void setEncrypted(boolean encrypted) { this.encrypted = encrypted; }
public String getData() { return data; }
public void setData(String data) { this.data = data; }
public Long getTimestamp() { return timestamp; }
public void setTimestamp(Long timestamp) { this.timestamp = timestamp; }
}
@PostMapping("/config")
@LogAnnotation
public String config(@RequestBody String rawData, String secret) {
Node node = nodeService.getOne(new QueryWrapper<Node>().eq("secret", secret));
if (node == null) return SUCCESS_RESPONSE;
try {
// 尝试解密数据
String decryptedData = decryptIfNeeded(rawData, secret);
// 解析为GostConfigDto
GostConfigDto gostConfigDto = JSON.parseObject(decryptedData, GostConfigDto.class);
checkGostConfigAsync.cleanNodeConfigs(node.getId().toString(), gostConfigDto);
log.info("🔓 节点 {} 配置数据接收成功{}", node.getId(), isEncryptedMessage(rawData) ? "(已解密)" : "");
} catch (Exception e) {
log.error("处理节点 {} 配置数据失败: {}", node.getId(), e.getMessage());
}
return SUCCESS_RESPONSE;
}
@RequestMapping("/test")
@LogAnnotation
@@ -78,32 +115,107 @@ public class FlowController extends BaseController {
/**
* 处理流量数据上报
*
* @param flowDataList 流量数据列表
* @param secret 节点密钥
* @param rawData 原始数据(可能是加密的)
* @param secret 节点密钥
* @return 处理结果
*/
@RequestMapping("/upload")
@LogAnnotation
public String uploadFlowData(@RequestBody List<FlowDto> flowDataList, String secret) {
public String uploadFlowData(@RequestBody String rawData, String secret) {
// 1. 验证节点权限
if (!isValidNode(secret)) {
return SUCCESS_RESPONSE;
}
if (flowDataList.isEmpty()) {
try {
// 2. 尝试解密数据
String decryptedData = decryptIfNeeded(rawData, secret);
// 3. 解析为FlowDto列表
List<FlowDto> flowDataList = JSON.parseArray(decryptedData, FlowDto.class);
if (flowDataList.isEmpty()) {
return SUCCESS_RESPONSE;
}
if (Objects.equals(flowDataList.get(0).getN(), "web_api")) {
return SUCCESS_RESPONSE;
}
// 记录日志
log.debug("🔓 节点流量数据接收成功{}",
isEncryptedMessage(rawData) ? "(已解密)" : "");
// 4. 处理流量数据
return processFlowData(flowDataList);
} catch (Exception e) {
log.error("处理流量数据失败: {}", e.getMessage(), e);
return SUCCESS_RESPONSE;
}
if(Objects.equals(flowDataList.get(0).getN(), "web_api")){
return SUCCESS_RESPONSE;
}
/**
* 检测消息是否为加密格式
*/
private boolean isEncryptedMessage(String data) {
try {
JSONObject json = JSON.parseObject(data);
return json.getBooleanValue("encrypted");
} catch (Exception e) {
return false;
}
}
/**
* 根据需要解密数据
*/
private String decryptIfNeeded(String rawData, String secret) {
if (rawData == null || rawData.trim().isEmpty()) {
throw new IllegalArgumentException("数据不能为空");
}
try {
// 尝试解析为加密消息格式
EncryptedMessage encryptedMessage = JSON.parseObject(rawData, EncryptedMessage.class);
if (encryptedMessage.isEncrypted() && encryptedMessage.getData() != null) {
// 获取或创建加密器
AESCrypto crypto = getOrCreateCrypto(secret);
if (crypto == null) {
log.warn("⚠️ 收到加密消息但无法创建解密器,使用原始数据");
return rawData;
}
// 解密数据
String decryptedData = crypto.decryptString(encryptedMessage.getData());
log.debug("🔓 数据解密成功");
return decryptedData;
}
} catch (Exception e) {
// 解析失败,可能是非加密格式,直接返回原始数据
log.debug("数据未加密或解密失败,使用原始数据: {}", e.getMessage());
}
return rawData;
}
/**
* 获取或创建加密器实例
*/
private AESCrypto getOrCreateCrypto(String secret) {
return CRYPTO_CACHE.computeIfAbsent(secret, AESCrypto::create);
}
/**
* 处理流量数据的核心逻辑
*/
private String processFlowData(List<FlowDto> flowDataList) {
// 2. 解析服务名称获取ID信息
String[] serviceIds = parseServiceName(flowDataList.get(0).getN());
String forwardId = serviceIds[0];
String userId = serviceIds[1];
String userTunnelId = serviceIds[2];
// 3. 一次性查询相关实体,避免后续重复查询
Forward forward = forwardService.getById(forwardId);
User user = userService.getById(userId);
@@ -111,16 +223,13 @@ public class FlowController extends BaseController {
if (!Objects.equals(userTunnelId, DEFAULT_USER_TUNNEL_ID)) {
userTunnel = userTunnelService.getById(userTunnelId);
}
// 4. 处理流量倍率
List<FlowDto> validFlowData = filterFlowData(flowDataList, forward);
// 5. 计算总流量
FlowStatistics flowStats = calculateTotalFlow(validFlowData);
// 6. 获取流量计费类型
int flowType = getFlowType(forward);
@@ -38,7 +38,4 @@ public class Forward extends BaseEntity{
private Long outFlow;
private Integer proxyProtocol;
}
@@ -835,7 +835,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
// 创建主服务
R serviceResult = createMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel, forward.getStrategy(), forward.getProxyProtocol());
R serviceResult = createMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel, forward.getStrategy());
if (serviceResult.getCode() != 0) {
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
if (nodeInfo.getOutNode() != null) {
@@ -1008,8 +1008,8 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
/**
* 创建主服务
*/
private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy, Integer proxy_protocol) {
GostDto result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, proxy_protocol);
private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy) {
GostDto result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy);
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
}
@@ -1049,10 +1049,10 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
* 更新主服务
*/
private R updateMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy) {
GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, forward.getProxyProtocol());
GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy);
if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) {
result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, forward.getProxyProtocol());
result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy);
}
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
@@ -499,7 +499,7 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
String serviceName = buildServiceName(forward.getId(), Long.valueOf(userId), userTunnel.getId());
// 6. 更新入口节点的主服务限速配置(使用批量UpdateService接口)
GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), speedId, forward.getRemoteAddr(), tunnel.getType(), tunnel, forward.getStrategy(), forward.getProxyProtocol());
GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), speedId, forward.getRemoteAddr(), tunnel.getType(), tunnel, forward.getStrategy());
}
}
}