From 306f2ed4d393ed7d78be47b6c3aef462d23baa8a Mon Sep 17 00:00:00 2001 From: = <=> Date: Tue, 5 Dec 2023 16:16:06 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=84=E5=88=99=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- neutrino-proxy-core/pom.xml | 5 ++ .../constant/SecurityRulePassTypeEnum.java | 5 +- .../server/dal/SecurityRuleMapper.java | 4 +- .../server/dal/entity/SecurityGroupDO.java | 2 +- ...{SecurityRule.java => SecurityRuleDO.java} | 27 +++---- .../proxy/core/TcpVisitorChannelHandler.java | 15 ++-- .../server/service/SecurityGroupService.java | 76 +++++++++++++++++++ .../src/main/resources/app.yml | 10 ++- .../resources/sql/mysql/init-structure.sql | 30 ++++++++ 9 files changed, 146 insertions(+), 28 deletions(-) rename neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/{SecurityRule.java => SecurityRuleDO.java} (81%) create mode 100644 neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/SecurityGroupService.java diff --git a/neutrino-proxy-core/pom.xml b/neutrino-proxy-core/pom.xml index fc566dbf..f29d6890 100644 --- a/neutrino-proxy-core/pom.xml +++ b/neutrino-proxy-core/pom.xml @@ -30,6 +30,11 @@ hutool-core ${hutool.version} + + cn.hutool + hutool-cache + ${hutool.version} + diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/SecurityRulePassTypeEnum.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/SecurityRulePassTypeEnum.java index 32d96759..60991e30 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/SecurityRulePassTypeEnum.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/SecurityRulePassTypeEnum.java @@ -6,8 +6,9 @@ import lombok.Getter; @AllArgsConstructor @Getter public enum SecurityRulePassTypeEnum { - REJECT(0, "reject"), - ALLOW(1, "allow") + DENY(-1, "DENY"), + ALLOW(1, "allow"), + NONE(0, "none") ; private final Integer code; diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/SecurityRuleMapper.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/SecurityRuleMapper.java index 03f9f1b4..9ded6d66 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/SecurityRuleMapper.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/SecurityRuleMapper.java @@ -1,7 +1,7 @@ package org.dromara.neutrinoproxy.server.dal; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.dromara.neutrinoproxy.server.dal.entity.SecurityRule; +import org.dromara.neutrinoproxy.server.dal.entity.SecurityRuleDO; -public interface SecurityRuleMapper extends BaseMapper { +public interface SecurityRuleMapper extends BaseMapper { } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityGroupDO.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityGroupDO.java index 9c07ff87..b1129c7b 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityGroupDO.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityGroupDO.java @@ -38,7 +38,7 @@ public class SecurityGroupDO { * 启用状态 * {@link EnableStatusEnum} */ - private Integer enable; + private EnableStatusEnum enable; /** * 创建时间 */ diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRule.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java similarity index 81% rename from neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRule.java rename to neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java index f72b895e..128940d4 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRule.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/SecurityRuleDO.java @@ -10,7 +10,6 @@ import lombok.ToString; import lombok.experimental.Accessors; import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; import org.dromara.neutrinoproxy.server.constant.SecurityRulePassTypeEnum; -import org.noear.solon.core.util.IpUtil; import java.util.Date; @@ -18,7 +17,7 @@ import java.util.Date; @ToString @Accessors(chain = true) @TableName("security_rule") -public class SecurityRule { +public class SecurityRuleDO { @TableId(type = IdType.AUTO) private Integer id; @@ -68,7 +67,7 @@ public class SecurityRule { * 启用状态 * {@link EnableStatusEnum} */ - private Integer enable; + private EnableStatusEnum enable; /** * 创建时间 */ @@ -83,16 +82,16 @@ public class SecurityRule { * @param ip * @return */ - public boolean allow(String ip) { + public SecurityRulePassTypeEnum allow(String ip) { - // 被判断的IP地址为空,不允许访问 + // 被判断的IP地址为空,不做判断 if (StrUtil.isEmpty(ip)) { - return false; + return SecurityRulePassTypeEnum.NONE; } // 没有规则,默认允许访问 if (StrUtil.isEmpty(rule)) { - return true; + return SecurityRulePassTypeEnum.ALLOW; } // ipv6只适配单ip形式 @@ -107,14 +106,16 @@ public class SecurityRule { // 单个ip,ipv6在此步已处理,后面不需要额外判断ipv6的情况 if (rule.matches("(\\d+\\.){3}\\d+") || isIpv6) { - return passType == SecurityRulePassTypeEnum.ALLOW && rule.equals(ip); + if (rule.equals(ip)) { + return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY; + } } // 范围类型 if (rule.matches("(\\d+\\.){3}\\d+-(\\d+\\.){3}\\d+")) { String[] ipRange = rule.split("-"); if (ipRange[0].compareTo(ip) <= 0 && ip.compareTo(ipRange[1]) <= 0) { - return passType == SecurityRulePassTypeEnum.ALLOW; + return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY; } } @@ -124,18 +125,18 @@ public class SecurityRule { Long beginIp = Ipv4Util.getBeginIpLong(netIp[0], Integer.valueOf(netIp[1])); Long endIp = Ipv4Util.getEndIpLong(netIp[0], Integer.valueOf(netIp[1])); if (beginIp <= ipLong && ipLong <= endIp) { - return passType == SecurityRulePassTypeEnum.ALLOW; + return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY; } } if (rule.equalsIgnoreCase("ALL") || rule.equals("0.0.0.0") || rule.equals("0..0.0.0/0")) { - return passType == SecurityRulePassTypeEnum.ALLOW; + return passType == SecurityRulePassTypeEnum.ALLOW ? SecurityRulePassTypeEnum.ALLOW : SecurityRulePassTypeEnum.DENY; } } - // 都没有匹配到,默认放行 - return true; + // 都没有匹配到 + return SecurityRulePassTypeEnum.NONE; } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java index 8f723802..3110dec7 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/core/TcpVisitorChannelHandler.java @@ -1,6 +1,11 @@ package org.dromara.neutrinoproxy.server.proxy.core; import cn.hutool.core.util.StrUtil; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOption; +import io.netty.channel.SimpleChannelInboundHandler; import lombok.extern.slf4j.Slf4j; import org.dromara.neutrinoproxy.core.Constants; import org.dromara.neutrinoproxy.core.ProxyMessage; @@ -8,11 +13,6 @@ import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum; import org.dromara.neutrinoproxy.server.proxy.domain.VisitorChannelAttachInfo; import org.dromara.neutrinoproxy.server.service.FlowReportService; import org.dromara.neutrinoproxy.server.util.ProxyUtil; -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOption; -import io.netty.channel.SimpleChannelInboundHandler; import org.noear.solon.Solon; import java.net.InetSocketAddress; @@ -62,8 +62,11 @@ public class TcpVisitorChannelHandler extends SimpleChannelInboundHandler securityGroupMap = new ConcurrentHashMap<>(); + + // 允许通过控制的缓存,缓存类型最近最久未使用缓存,容量100,超时时间5分钟 + private Cache ipAllowControlCache = CacheUtil.newLRUCache(100, 1000 * 60 * 5); + + public void init() { + List groupDOList = securityGroupMapper.selectList(Wrappers.lambdaQuery(SecurityGroupDO.class)); + groupDOList.forEach(securityGroupDO -> securityGroupMap.put(securityGroupDO.getId(), securityGroupDO)); + } + + public boolean judgeAllow(String ip, Integer groupId) { + + SecurityGroupDO groupDO = securityGroupMap.get(groupId); + if (groupDO == null || groupDO.getEnable() == EnableStatusEnum.DISABLE) { + return true; + } + + String judgeAllowMapKey = ip + groupId; + if (ipAllowControlCache.containsKey(judgeAllowMapKey)) { + return ipAllowControlCache.get(judgeAllowMapKey); + } + + List ruleDOList = securityRuleMapper.selectList(Wrappers.lambdaQuery(SecurityRuleDO.class) + .eq(SecurityRuleDO::getGroupId, groupId) + .orderByAsc(SecurityRuleDO::getPriority) + ); + Boolean allow = null; + for (SecurityRuleDO ruleDO : ruleDOList) { + SecurityRulePassTypeEnum passType = ruleDO.allow(ip); + if (passType == SecurityRulePassTypeEnum.ALLOW) { + allow = true; + break; + } + if (passType == SecurityRulePassTypeEnum.DENY) { + allow = false; + break; + } + } + + if (allow == null) { + allow = true; + } + + // 当前IP没有匹配到任何一条规则,则放行 + ipAllowControlCache.put(judgeAllowMapKey, allow); + + return allow; + } +} diff --git a/neutrino-proxy-server/src/main/resources/app.yml b/neutrino-proxy-server/src/main/resources/app.yml index 665cfc83..cb798520 100644 --- a/neutrino-proxy-server/src/main/resources/app.yml +++ b/neutrino-proxy-server/src/main/resources/app.yml @@ -74,13 +74,15 @@ neutrino: data: db: # 数据库类型,目前支持h2、mysql、mariadb - type: ${DB_TYPE:h2} + # type: ${DB_TYPE:h2} + type: ${DB_TYPE:mysql} # 数据库连接URL - url: ${DB_URL:jdbc:h2:file:./data/db;MODE=MySQL;AUTO_SERVER=TRUE} + # url: ${DB_URL:jdbc:h2:file:./data/db;MODE=MySQL;AUTO_SERVER=TRUE} + url: ${DB_URL:jdbc:mysql://okfly.vip:37889/neutrino-proxy?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true} # 数据库用户名 - username: ${DB_USER:} + username: ${DB_USER:root} # 数据库密码 - password: ${DB_PASSWORD:} + password: ${DB_PASSWORD:Root1234@} #添加MIME印射(如果有需要?) #是否启用静态文件服务。(可不配,默认为启用) diff --git a/neutrino-proxy-server/src/main/resources/sql/mysql/init-structure.sql b/neutrino-proxy-server/src/main/resources/sql/mysql/init-structure.sql index c6c0037b..f763edd0 100644 --- a/neutrino-proxy-server/src/main/resources/sql/mysql/init-structure.sql +++ b/neutrino-proxy-server/src/main/resources/sql/mysql/init-structure.sql @@ -50,6 +50,35 @@ CREATE TABLE IF NOT EXISTS `port_group` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4; +#安全组 +CREATE TABLE IF NOT EXISTS `security_group` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `name` varchar(20) NOT NULL COMMENT '安全组名称', + `description` varchar(255) COMMENT '安全组描述', + `user_id` int NOT NULL COMMENT '用户ID', + `enable` int(1) NOT NULL COMMENT '启用状态', + `create_time` datetime(3) NOT NULL COMMENT '创建时间', + `update_time` datetime(3) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +#安全组规则 +CREATE TABLE IF NOT EXISTS `security_rule` ( + `id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `group_id` int NOT NULL COMMENT '关联安全组', + `name` varchar(20) NOT NULL COMMENT '规则名称', + `description` varchar(255) NOT NULL COMMENT '规则描述', + `rule` text NOT NULL COMMENT '规则内容', + `pass_type` int(1) NOT NULL COMMENT '放行类型', + `priority` int(1) NOT NULL COMMENT '优先级', + `user_id` int NOT NULL COMMENT '用户ID', + `enable` int(1) NOT NULL COMMENT '启用状态', + `create_time` datetime(3) NOT NULL COMMENT '创建时间', + `update_time` datetime(3) NOT NULL COMMENT '更新时间', + PRIMARY KEY (`id`), + KEY `I_group_id_priority` (`group_id`, `priority`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + #############################代理配置相关表############################# #license表 CREATE TABLE IF NOT EXISTS `license` ( @@ -207,3 +236,4 @@ CREATE TABLE IF NOT EXISTS `flow_report_month` ( KEY `I_flow_report_month_user_id` (`user_id`), KEY `I_flow_report_month_license_id` (`license_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +