diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java index 60f89e23..eb48ca3b 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/base/proxy/ProxyConfig.java @@ -63,7 +63,6 @@ public class ProxyConfig { public static class Tcp { private Integer bossThreadCount; private Integer workThreadCount; - private String domainName; private Integer httpProxyPort; private Integer httpsProxyPort; private String keyStorePassword; diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java index e66cd7a3..7bdca2a0 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/constant/ExceptionConstant.java @@ -59,7 +59,6 @@ public enum ExceptionConstant { PORT_MAPPING_NOT_EXIST(14000, "端口映射记录不存在"), PORT_CANNOT_REPEAT_MAPPING(14001, "服务端口[{}]不能重复映射"), AN_UNSUPPORTED_PROTOCOL(14002, "不支持的协议[{}]!"), - PORT_MAPPING_SUBDONAME_CONNOT_REPEAT(14003, "子域名不能重复使用!"), // 调度管理(15000) JOB_INFO_NOT_EXIST(15000, "调度管理记录不存在"), SYSTEM_ERROR(500, "系统异常"), @@ -73,6 +72,10 @@ public enum ExceptionConstant { SECURITY_RULE_NOT_EXIST(17001, "安全规则不存在"), // 域名映射管理(18000) DOMAIN_NAME_CANNOT_REPEAT(18000, "域名不能重复"), + DOMAIN_NAME_NOT_EXIST(18001, "域名不存在"), + DOMAIN_NAME_IS_DISABLE(18002, "当前域名[{}]被禁用"), + SUDOMAIN_NAME_CANNOT_REPEAT(18003, "子域名不能重复"), + DOMAIN_NAME_IS_USED(18002, "当前域名正在使用"), ; private int code; diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java index 3f64d661..1f3d18a6 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/DomainNameController.java @@ -21,7 +21,8 @@ public class DomainNameController { @Get @Mapping("/bind-info") public String bindInfo () { - return proxyConfig.getServer().getTcp().getDomainName(); +// return proxyConfig.getServer().getTcp().getDomainName(); + return null; } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/PortMappingController.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/PortMappingController.java index c7fc8755..dcfb74b1 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/PortMappingController.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/PortMappingController.java @@ -52,7 +52,7 @@ public class PortMappingController { ParamCheckUtil.checkNotNull(networkProtocolEnum, ExceptionConstant.AN_UNSUPPORTED_PROTOCOL, req.getProtocal()); if (networkProtocolEnum != NetworkProtocolEnum.HTTP) { // 目前仅HTTP支持绑定域名 - req.setSubdomain(null); + req.setDomainMappings(null); } req.setProtocal(networkProtocolEnum.getDesc()); if (null == req.getProxyResponses()) { @@ -87,7 +87,7 @@ public class PortMappingController { ParamCheckUtil.checkNotNull(networkProtocolEnum, ExceptionConstant.AN_UNSUPPORTED_PROTOCOL, req.getProtocal()); if (networkProtocolEnum != NetworkProtocolEnum.HTTP) { // 目前仅HTTP支持绑定域名 - req.setSubdomain(null); + req.setDomainMappings(null); } req.setProtocal(networkProtocolEnum.getDesc()); if (null == req.getProxyResponses()) { diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateEnableStatusReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateEnableStatusReq.java index ca25274c..1ac3e549 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateEnableStatusReq.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/DomainUpdateEnableStatusReq.java @@ -18,4 +18,9 @@ public class DomainUpdateEnableStatusReq { * 启用状态 */ private Integer enable; + + /** + * 域名 + */ + private String domain; } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java index 87ae362d..c701771c 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingCreateReq.java @@ -23,6 +23,8 @@ package org.dromara.neutrinoproxy.server.controller.req.proxy; import lombok.Data; +import java.util.List; + /** * 端口映射创建请求 * @author: aoshiguchen @@ -38,10 +40,6 @@ public class PortMappingCreateReq { * 协议 */ private String protocal; - /** - * 子域名 - */ - private String subdomain; /** * 服务端端口 */ @@ -80,4 +78,15 @@ public class PortMappingCreateReq { * 描述 */ private String description; + + /** + * 域名映射列表 + */ + private List domainMappings; + + @Data + public class DomainMapping { + private Integer domainId; + private String subdomain; + } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingUpdateReq.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingUpdateReq.java index 31f85a01..a91b6471 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingUpdateReq.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/controller/req/proxy/PortMappingUpdateReq.java @@ -23,6 +23,8 @@ package org.dromara.neutrinoproxy.server.controller.req.proxy; import lombok.Data; +import java.util.List; + /** * 端口映射更新请求 * @author: aoshiguchen @@ -42,10 +44,6 @@ public class PortMappingUpdateReq { * 协议 */ private String protocal; - /** - * 子域名 - */ - private String subdomain; /** * 服务端端口 */ @@ -84,4 +82,15 @@ public class PortMappingUpdateReq { * 描述 */ private String description; + + /** + * 域名映射列表 + */ + private List domainMappings; + + @Data + public class DomainMapping { + private Integer domainId; + private String subdomain; + } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java index 12d2b6ff..3cc183d3 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainMapper.java @@ -2,16 +2,13 @@ package org.dromara.neutrinoproxy.server.dal; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import org.apache.ibatis.annotations.Mapper; -import org.dromara.neutrinoproxy.server.controller.req.system.PortGroupListReq; -import org.dromara.neutrinoproxy.server.controller.res.system.PortGroupListRes; +import org.apache.ibatis.annotations.Param; import org.dromara.neutrinoproxy.server.dal.entity.DomainNameDO; -import org.dromara.neutrinoproxy.server.dal.entity.PortGroupDO; +import org.dromara.neutrinoproxy.server.service.bo.FullDomainNameBO; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -21,7 +18,7 @@ public interface DomainMapper extends BaseMapper { default DomainNameDO checkRepeat(String domain, Set excludeIds) { return this.selectOne(new LambdaQueryWrapper() .eq(DomainNameDO::getDomain, domain) - .notIn(DomainNameDO::getId, excludeIds) + .notIn(excludeIds != null, DomainNameDO::getId, excludeIds) .last("limit 1") ); } @@ -33,4 +30,11 @@ public interface DomainMapper extends BaseMapper { .set(DomainNameDO::getUpdateTime, updateTime) ); } + + + List selectFullDomainNameListByPortMappingIds(@Param("ids") Set ids); + + List selectFullDomainNameListByDomainNameIds(@Param("ids") Set ids); + + List selectFullDomainNameList(); } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainPortMappingMapper.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainPortMappingMapper.java new file mode 100644 index 00000000..beb62ab9 --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/DomainPortMappingMapper.java @@ -0,0 +1,48 @@ +package org.dromara.neutrinoproxy.server.dal; + +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.neutrinoproxy.server.dal.entity.DomainNameDO; +import org.dromara.neutrinoproxy.server.dal.entity.DomainPortMappingDO; +import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO; +import org.dromara.neutrinoproxy.server.service.bo.FullDomainNameBO; + +import java.util.Date; +import java.util.List; +import java.util.Set; + +/** + * @author: Mirac + * @date: 2024/8/24 + */ +@Mapper +public interface DomainPortMappingMapper extends BaseMapper { + default Boolean checkRepeatBySubdomain(String subdomain, Integer domainNameId, Integer portMappingId) { + return StringUtils.isEmpty(subdomain)? Boolean.FALSE : this.selectCount(new LambdaQueryWrapper() + .eq(DomainPortMappingDO::getSubdomain, subdomain) + .eq(DomainPortMappingDO::getDomainNameId, domainNameId) + .ne(portMappingId != null, DomainPortMappingDO::getPortMappingId, portMappingId) + .last("limit 1") + ).intValue() > 0; + } + + default List findByPortMappingId(Integer portMappingId) { + return this.selectList(Wrappers.lambdaQuery() + .eq(DomainPortMappingDO::getPortMappingId, portMappingId)); + } + + default void deleteByDomainNameId(Integer doomainNameId) { + this.delete(Wrappers.lambdaQuery() + .eq(DomainPortMappingDO::getDomainNameId, doomainNameId)); + } + + default boolean checkUsed(Integer domainNameId) { + return this.exists(Wrappers.lambdaQuery() + .eq(DomainPortMappingDO::getDomainNameId, domainNameId)); + } +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/PortMappingMapper.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/PortMappingMapper.java index b240047e..4b02a106 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/PortMappingMapper.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/PortMappingMapper.java @@ -45,18 +45,6 @@ public interface PortMappingMapper extends BaseMapper { ); } - /** - * 校验子域名是否重复 - * @param subdomain - * @return - */ - default Boolean checkRepeatBySubdomain(String subdomain, Set excludeIds) { - return StringUtils.isEmpty(subdomain)? Boolean.FALSE : this.selectCount(new LambdaQueryWrapper() - .eq(PortMappingDO::getSubdomain, subdomain) - .notIn(!CollectionUtil.isEmpty(excludeIds), PortMappingDO::getId, excludeIds) - ).intValue() > 0; - } - default List findEnableListByLicenseId(Integer licenseId) { return this.selectList(new LambdaQueryWrapper() .eq(PortMappingDO::getLicenseId, licenseId) @@ -109,6 +97,4 @@ public interface PortMappingMapper extends BaseMapper { } List selectPortMappingByCondition(IPage page, @Param("req") PortMappingListReq req); - - List selectFullDomainNameListByIds(@Param("ids") Set ids); } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/DomainPortMappingDO.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/DomainPortMappingDO.java new file mode 100644 index 00000000..9ff8026e --- /dev/null +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/DomainPortMappingDO.java @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.dromara.neutrinoproxy.server.dal.entity; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; +import org.dromara.neutrinoproxy.server.constant.OnlineStatusEnum; +import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingListRes; + +import java.util.Date; + +/** + * @author: Mirac + * @date: 2024/8/24 + */ +@ToString +@Accessors(chain = true) +@Data +@TableName("domain_port_mapping") +public class DomainPortMappingDO { + + /** + * 主键 + */ + @TableId(type = IdType.AUTO) + private Integer id; + /** + * 端口映射id + */ + private Integer portMappingId; + /** + * 域名id + */ + private Integer domainNameId; + /** + * 子域名 + */ + private String subdomain; +} diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/PortMappingDO.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/PortMappingDO.java index 16745fda..75ba84a1 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/PortMappingDO.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/dal/entity/PortMappingDO.java @@ -54,10 +54,6 @@ public class PortMappingDO { * 协议 */ private String protocal; - /** - * 子域名 - */ - private String subdomain; /** * 服务端端口 */ diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java index ce1197db..80d4bf68 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpProxy.java @@ -30,7 +30,7 @@ public class HttpProxy implements EventListener { private ProxyConfig proxyConfig; @Override public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable { - if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName()) || null == proxyConfig.getServer().getTcp().getHttpProxyPort()) { + if (null == proxyConfig.getServer().getTcp().getHttpProxyPort()) { log.info("no config domain name,nonsupport http proxy."); return; } @@ -48,7 +48,7 @@ public class HttpProxy implements EventListener { ch.pipeline().addFirst(new LoggingHandler(HttpProxy.class)); } ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new HttpVisitorSecurityChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); + ch.pipeline().addLast(new HttpVisitorSecurityChannelHandler(false)); ch.pipeline().addLast("flowLimiter",new VisitorFlowLimiterChannelHandler()); ch.pipeline().addLast(new HttpVisitorChannelHandler()); } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java index aa5ff58e..19e2e7c0 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/enhance/HttpsProxy.java @@ -37,7 +37,7 @@ public class HttpsProxy implements EventListener { private ProxyConfig proxyConfig; @Override public void onEvent(AppLoadEndEvent appLoadEndEvent) throws Throwable { - if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName()) || null == proxyConfig.getServer().getTcp().getHttpsProxyPort() || + if (null == proxyConfig.getServer().getTcp().getHttpsProxyPort() || StringUtils.isEmpty(proxyConfig.getServer().getTcp().getJksPath()) || StringUtils.isEmpty(proxyConfig.getServer().getTcp().getKeyStorePassword())) { log.info("no config domain name,nonsupport https proxy."); return; @@ -57,7 +57,7 @@ public class HttpsProxy implements EventListener { } ch.pipeline().addLast(createSslHandler()); ch.pipeline().addFirst(new BytesMetricsHandler()); - ch.pipeline().addLast(new HttpVisitorSecurityChannelHandler(proxyConfig.getServer().getTcp().getDomainName())); + ch.pipeline().addLast(new HttpVisitorSecurityChannelHandler(true)); ch.pipeline().addLast("flowLimiter",new VisitorFlowLimiterChannelHandler()); ch.pipeline().addLast(new HttpVisitorChannelHandler()); } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java index eccd2914..9c3d56c6 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/proxy/security/HttpVisitorSecurityChannelHandler.java @@ -1,6 +1,6 @@ package org.dromara.neutrinoproxy.server.proxy.security; -import cn.hutool.core.util.StrUtil; +import com.google.errorprone.annotations.Var; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @@ -9,6 +9,7 @@ import org.apache.commons.lang3.StringUtils; import org.dromara.neutrinoproxy.core.Constants; import org.dromara.neutrinoproxy.core.util.HttpUtil; import org.dromara.neutrinoproxy.core.util.IpUtil; +import org.dromara.neutrinoproxy.server.service.DomainService; import org.dromara.neutrinoproxy.server.service.PortMappingService; import org.dromara.neutrinoproxy.server.service.SecurityGroupService; import org.dromara.neutrinoproxy.server.util.ProxyUtil; @@ -22,22 +23,17 @@ import org.noear.solon.Solon; public class HttpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdapter { private final SecurityGroupService securityGroupService = Solon.context().getBean(SecurityGroupService.class); private final PortMappingService portMappingService = Solon.context().getBean(PortMappingService.class); + private final DomainService domainService = Solon.context().getBean(DomainService.class); /** * 域名 */ - private String domainName; + private Boolean isHttps; - public HttpVisitorSecurityChannelHandler(String domainName) { - this.domainName = domainName; + public HttpVisitorSecurityChannelHandler(Boolean isHttps) { + this.isHttps = isHttps; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // 未配置域名则不支持通过域名访问 - if (StrUtil.isBlank(domainName)) { - ctx.channel().close(); - return; - } - ByteBuf buf = (ByteBuf) msg; Integer serverPort = ctx.channel().attr(Constants.SERVER_PORT).get(); @@ -46,24 +42,28 @@ public class HttpVisitorSecurityChannelHandler extends ChannelInboundHandlerAdap byte[] bytes = new byte[buf.readableBytes()]; buf.readBytes(bytes); String httpContent = new String(bytes); - String host = HttpUtil.getHostIgnorePort(httpContent); + String host = HttpUtil.getHostIgnorePort(httpContent); //test1.asgc.fun log.debug("HttpProxy host: {}", host); if (StringUtils.isBlank(host)) { ctx.channel().close(); return; } - - // 根据Host匹配端口映射 - if (!host.endsWith(domainName)) { +// int index = host.lastIndexOf("." + domainName); +// String subdomain = host.substring(0, index); + // 判断域名是否被禁用 + Integer domainNameId = ProxyUtil.getDomainNameIdByFullDomain(host); + if (domainNameId == null) { + ctx.channel().close(); + return; + } + // 域名映射强制https验证 + if (!isHttps && domainService.isOnlyHttps(domainNameId)) { ctx.channel().close(); return; } - int index = host.lastIndexOf("." + domainName); - String subdomain = host.substring(0, index); - // 根据域名拿到绑定的映射对应的cmdChannel - serverPort = ProxyUtil.getServerPortBySubdomain(subdomain); + serverPort = ProxyUtil.getServerPortByFullDomain(host); if (null == serverPort) { ctx.channel().close(); return; diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java index aafd9a72..ffc2cffd 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/DomainService.java @@ -1,9 +1,12 @@ package org.dromara.neutrinoproxy.server.service; -import cn.hutool.core.bean.BeanUtil; +import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.text.StrPool; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.solon.plugins.pagination.Page; @@ -13,21 +16,24 @@ import org.apache.ibatis.solon.annotation.Db; import org.dromara.neutrinoproxy.server.base.page.PageInfo; import org.dromara.neutrinoproxy.server.base.page.PageQuery; import org.dromara.neutrinoproxy.server.base.rest.SystemContextHolder; -import org.dromara.neutrinoproxy.server.constant.DefaultDomainStatusEnum; -import org.dromara.neutrinoproxy.server.constant.EnableStatusEnum; -import org.dromara.neutrinoproxy.server.constant.ExceptionConstant; -import org.dromara.neutrinoproxy.server.constant.HttpsStatusEnum; +import org.dromara.neutrinoproxy.server.constant.*; import org.dromara.neutrinoproxy.server.controller.req.proxy.*; import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainListRes; import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainUpdateDefaultStatusRes; import org.dromara.neutrinoproxy.server.controller.res.proxy.DomainUpdateEnableStatusRes; import org.dromara.neutrinoproxy.server.dal.DomainMapper; +import org.dromara.neutrinoproxy.server.dal.DomainPortMappingMapper; +import org.dromara.neutrinoproxy.server.dal.PortMappingMapper; import org.dromara.neutrinoproxy.server.dal.UserMapper; import org.dromara.neutrinoproxy.server.dal.entity.DomainNameDO; +import org.dromara.neutrinoproxy.server.dal.entity.DomainPortMappingDO; +import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO; import org.dromara.neutrinoproxy.server.dal.entity.UserDO; +import org.dromara.neutrinoproxy.server.service.bo.FullDomainNameBO; import org.dromara.neutrinoproxy.server.util.ParamCheckUtil; -import org.h2.schema.Domain; +import org.dromara.neutrinoproxy.server.util.ProxyUtil; import org.noear.solon.annotation.Component; +import org.noear.solon.annotation.Init; import org.noear.solon.core.handle.UploadedFile; import java.io.ByteArrayOutputStream; @@ -48,13 +54,13 @@ public class DomainService { private DomainMapper domainMapper; @Db private UserMapper userMapper; + @Db + private PortMappingMapper portMappingMapper; + @Db + private DomainPortMappingMapper domainPortMappingMapper; + public PageInfo page(PageQuery pageQuery, DomainListReq req) { -// Page page = new Page<>(pageQuery.getCurrent(), pageQuery.getSize()); -// LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery() -// .eq(req.getEnable() != null, DomainNameDO::getEnable, req.getEnable()) -// .eq(req.getUserId() != null, DomainNameDO::getUserId, req.getUserId()); -// Page page = domainMapper.selectPage(page, queryWrapper); Page page = domainMapper.selectPage(new Page<>(pageQuery.getCurrent(), pageQuery.getSize()), new LambdaQueryWrapper() .eq(req.getEnable() != null, DomainNameDO::getEnable, req.getEnable()) .eq(req.getUserId() != null, DomainNameDO::getUserId, req.getUserId()) @@ -98,9 +104,12 @@ public class DomainService { /** * 创建域名 + * * @param req */ public void create(DomainCreateReq req, UploadedFile jks) throws IOException { + DomainNameDO domainNameCheck = domainMapper.checkRepeat(req.getDomain(), null); + ParamCheckUtil.checkMustNull(domainNameCheck, ExceptionConstant.DOMAIN_NAME_CANNOT_REPEAT); DomainNameDO domainNameDO = new DomainNameDO(); if (null != jks) { ParamCheckUtil.checkNotEmpty(req.getKeyStorePassword(), "keyStorePassword"); @@ -114,17 +123,19 @@ public class DomainService { Date now = new Date(); domainNameDO.setDomain(req.getDomain()); domainNameDO.setIsDefault(DefaultDomainStatusEnum.DISABLE.getStatus()); - domainNameDO.setForceHttps(req.getForceHttps() != null ? HttpsStatusEnum.of(req.getForceHttps()).getStatus(): HttpsStatusEnum.DISABLE_ONLY_HTTPS.getStatus()); + domainNameDO.setForceHttps(req.getForceHttps() != null ? HttpsStatusEnum.of(req.getForceHttps()).getStatus() : HttpsStatusEnum.DISABLE_ONLY_HTTPS.getStatus()); domainNameDO.setUserId(userId); domainNameDO.setEnable(EnableStatusEnum.ENABLE.getStatus()); domainNameDO.setCreateTime(now); domainNameDO.setUpdateTime(now); domainMapper.insert(domainNameDO); + //更新主域名到域名id映射 + ProxyUtil.setDomainToDomainNameId(domainNameDO.getDomain(), domainNameDO.getId()); } /** * 将 InputStream 转换为 byte[] - */ + */ private byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); int nRead; @@ -136,10 +147,12 @@ public class DomainService { return buffer.toByteArray(); } - //TODO 更新相关channel public void update(DomainUpdateReq req, UploadedFile jks) throws IOException { DomainNameDO domainNameCheck = domainMapper.checkRepeat(req.getDomain(), Sets.newHashSet(req.getId())); ParamCheckUtil.checkMustNull(domainNameCheck, ExceptionConstant.DOMAIN_NAME_CANNOT_REPEAT); + + DomainNameDO oldDomainNameDO = domainMapper.selectOne(Wrappers.lambdaQuery().eq(DomainNameDO::getId, req.getId())); + List oldFullDomainNameBOS = domainMapper.selectFullDomainNameListByDomainNameIds(Sets.newHashSet(req.getId())); LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(DomainNameDO::getId, req.getId()); if (null != jks) { @@ -153,23 +166,49 @@ public class DomainService { updateWrapper.set(DomainNameDO::getDomain, req.getDomain()); updateWrapper.set(DomainNameDO::getUpdateTime, new Date()); updateWrapper.set(req.getForceHttps() != null, DomainNameDO::getForceHttps, req.getForceHttps()); - int update = domainMapper.update(updateWrapper); - System.out.println(update); + domainMapper.update(updateWrapper); + + //更新域名相关映射 + if (!Objects.equals(oldDomainNameDO.getDomain(), req.getDomain())) { + //更新主域名到域名id映射 + ProxyUtil.removeDomainToDomainNameId(oldDomainNameDO.getDomain()); + ProxyUtil.setDomainToDomainNameId(req.getDomain(), req.getId()); + if (CollectionUtil.isEmpty(oldFullDomainNameBOS)) return; + //更新完整域名到服务器端口映射 + for (FullDomainNameBO oldFullDomainNameBO : oldFullDomainNameBOS) { + String oldFullDomain = StrUtil.join(StrPool.DOT, oldFullDomainNameBO.getSubdomain(), oldFullDomainNameBO.getDomain()); + Integer serverPort = ProxyUtil.getServerPortByFullDomain(oldFullDomain); + ProxyUtil.removeFullDomainToServerPort(oldFullDomain); + ProxyUtil.setFullDomainToServerPort(StrUtil.join(StrPool.DOT, oldFullDomainNameBO.getSubdomain(), req.getDomain()), serverPort); + } + } } - // TODO 更新对应的channel public DomainUpdateEnableStatusRes updateEnableStatus(DomainUpdateEnableStatusReq req) { if (Objects.equals(req.getEnable(), EnableStatusEnum.DISABLE.getStatus())) { //如果设置状态为禁用,则将默认域名设置为非默认域名 updateDefaultStatus(req.getId(), DefaultDomainStatusEnum.DISABLE.getStatus()); } domainMapper.updateEnableStatus(req.getId(), req.getEnable(), new Date()); + if (Objects.equals(req.getEnable(), EnableStatusEnum.ENABLE.getStatus())) { + ProxyUtil.setDomainToDomainNameId(req.getDomain(), req.getId()); + } else { + ProxyUtil.removeDomainToDomainNameId(req.getDomain()); + } return new DomainUpdateEnableStatusRes(); } - //TODO 处理VisitorChannel - public void delete(Integer id) { - domainMapper.deleteById(id); + public void delete(Integer domainNameId) { + //检查当前域名是否正在使用 + boolean checkUsed = domainPortMappingMapper.checkUsed(domainNameId); + ParamCheckUtil.checkExpression(!checkUsed, ExceptionConstant.DOMAIN_NAME_IS_USED); + DomainNameDO domainNameDO = domainMapper.selectOne(Wrappers.lambdaQuery() + .eq(DomainNameDO::getId, domainNameId) + .select(DomainNameDO::getDomain, DomainNameDO::getEnable)); + if (Objects.equals(domainNameDO.getEnable(), EnableStatusEnum.ENABLE.getStatus())) { + ProxyUtil.removeDomainToDomainNameId(domainNameDO.getDomain()); + } + domainMapper.deleteById(domainNameId); } public DomainUpdateDefaultStatusRes updateDefaultStatus(Integer id, Integer isDefault) { @@ -192,4 +231,43 @@ public class DomainService { return new DomainUpdateDefaultStatusRes(); } + @Init + public void init() { + List fullDomainNameBOS = domainMapper.selectFullDomainNameList(); + if (CollectionUtil.isEmpty(fullDomainNameBOS)) { + return; + } + Set portMappingIds = fullDomainNameBOS.stream().map(FullDomainNameBO::getPortMappingId).collect(Collectors.toSet()); + List portMappingDOS = portMappingMapper.selectBatchIds(portMappingIds); + if (CollectionUtil.isEmpty(portMappingDOS)) { + return; + } + Map portMappingDOMap = portMappingDOS.stream() + .filter(item -> NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal())) + .collect(Collectors.toMap(PortMappingDO::getId, Function.identity())); + fullDomainNameBOS.forEach(item -> { + PortMappingDO portMappingDO = portMappingDOMap.get(item.getPortMappingId()); + if (null == portMappingDO) { + return; + } + String fullDomain = StrUtil.join(StrPool.DOT, item.getSubdomain(), item.getDomain()); + //更新完整域名到端口映射 + ProxyUtil.setFullDomainToServerPort(fullDomain, portMappingDO.getServerPort()); + }); + + //更新主域名到主域名id缓存,未禁用 + List domainNameDOS = domainMapper.selectList(Wrappers.lambdaQuery() + .eq(DomainNameDO::getEnable, EnableStatusEnum.ENABLE.getStatus()) + .select(DomainNameDO::getDomain, DomainNameDO::getId)); + for (DomainNameDO domainNameDO : domainNameDOS) { + ProxyUtil.setDomainToDomainNameId(domainNameDO.getDomain(), domainNameDO.getId()); + } + } + + public boolean isOnlyHttps(Integer domainNameId) { + ParamCheckUtil.checkExpression((domainNameId != null && domainNameId > 0), ExceptionConstant.DOMAIN_NAME_NOT_EXIST); + DomainNameDO domainNameDO = domainMapper.selectOne(Wrappers.lambdaQuery() + .eq(DomainNameDO::getId, domainNameId)); + return domainNameDO != null && Objects.equals(domainNameDO.getForceHttps(), HttpsStatusEnum.ONLY_HTTPS.getStatus()); + } } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java index 606181b7..812984d2 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/PortMappingService.java @@ -2,8 +2,8 @@ package org.dromara.neutrinoproxy.server.service; import cn.hutool.cache.Cache; import cn.hutool.cache.CacheUtil; -import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.text.StrPool; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; @@ -30,15 +30,8 @@ import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingCreateRe import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingDetailRes; import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingListRes; import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingUpdateEnableStatusRes; -import org.dromara.neutrinoproxy.server.controller.res.proxy.PortMappingUpdateRes; -import org.dromara.neutrinoproxy.server.dal.LicenseMapper; -import org.dromara.neutrinoproxy.server.dal.PortMappingMapper; -import org.dromara.neutrinoproxy.server.dal.PortPoolMapper; -import org.dromara.neutrinoproxy.server.dal.UserMapper; -import org.dromara.neutrinoproxy.server.dal.entity.LicenseDO; -import org.dromara.neutrinoproxy.server.dal.entity.PortMappingDO; -import org.dromara.neutrinoproxy.server.dal.entity.PortPoolDO; -import org.dromara.neutrinoproxy.server.dal.entity.UserDO; +import org.dromara.neutrinoproxy.server.dal.*; +import org.dromara.neutrinoproxy.server.dal.entity.*; import org.dromara.neutrinoproxy.server.service.bo.FlowLimitBO; import org.dromara.neutrinoproxy.server.service.bo.FullDomainNameBO; import org.dromara.neutrinoproxy.server.util.ParamCheckUtil; @@ -69,6 +62,10 @@ public class PortMappingService implements LifecycleBean { private UserMapper userMapper; @Db private PortPoolMapper portPoolMapper; + @Db + private DomainMapper domainMapper; + @Db + private DomainPortMappingMapper domainPortMappingMapper; @Inject private VisitorChannelService visitorChannelService; @@ -83,7 +80,6 @@ public class PortMappingService implements LifecycleBean { /** 端口到安全组Id的映射 */ private final Map mappingPortToSecurityGroupMap = new ConcurrentHashMap<>(); - // 服务端端口到端口映射id的映射 private final Cache serverPortToPortMappingIdCache = CacheUtil.newLRUCache(500, 1000 * 60 * 10); // 端口映射id到licenseId private final Cache idToLicenseIdCache = CacheUtil.newLRUCache(500, 1000 * 60 * 10); @@ -121,13 +117,10 @@ public class PortMappingService implements LifecycleBean { //域名相关 Set portMappingIds = respList.stream().map(PortMappingListRes::getId).collect(Collectors.toSet()); - List fullDomainNameBOS = portMappingMapper.selectFullDomainNameListByIds(portMappingIds); - Map> fullDomainNameBOMap = fullDomainNameBOS.stream().collect(Collectors.groupingBy(FullDomainNameBO::getId)); + List fullDomainNameBOS = domainMapper.selectFullDomainNameListByPortMappingIds(portMappingIds); + Map> fullDomainNameBOMap = fullDomainNameBOS.stream().collect(Collectors.groupingBy(FullDomainNameBO::getPortMappingId)); respList.forEach(item -> { - //TODO 删除subdomain字段 - item.setSubdomains(null); - LicenseDO license = licenseMap.get(item.getLicenseId()); if (null == license) { return; @@ -140,10 +133,11 @@ public class PortMappingService implements LifecycleBean { } item.setUserName(user.getName()); List bos = fullDomainNameBOMap.get(item.getId()); + // TODO 后续重构域名映射返回值结构 if (CollectionUtil.isNotEmpty(bos)) { List subdomains = bos.stream().map(FullDomainNameBO::getSubdomain).collect(Collectors.toList()); List domains = bos.stream().map(FullDomainNameBO::getDomain).collect(Collectors.toList()); - List domainIds = bos.stream().map(FullDomainNameBO::getDomainId).collect(Collectors.toList()); + List domainIds = bos.stream().map(FullDomainNameBO::getDomainNameId).collect(Collectors.toList()); item.setSubdomains(subdomains); item.setDomains(domains); item.setDomainIds(domainIds); @@ -169,13 +163,24 @@ public class PortMappingService implements LifecycleBean { PortPoolDO portPoolDO = portPoolMapper.findByPort(req.getServerPort()); ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST); ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), null), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort()); - ParamCheckUtil.checkExpression(!portMappingMapper.checkRepeatBySubdomain(req.getSubdomain(), null), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT); - + //验证域名映射相关参数条件 + Set domainIds = req.getDomainMappings().stream().map(item -> item.getDomainId()).collect(Collectors.toSet()); + List domainNameDOS = domainMapper.selectBatchIds(domainIds); + Map domainNameDOMap = domainNameDOS.stream().collect(Collectors.toMap(DomainNameDO::getId, Function.identity())); + if (NetworkProtocolEnum.isHttp(req.getProtocal()) && CollectionUtil.isNotEmpty(req.getDomainMappings())) { + req.getDomainMappings().forEach(item -> { + DomainNameDO domainNameDO = domainNameDOMap.get(item.getDomainId()); + ParamCheckUtil.checkNotNull(domainNameDO, ExceptionConstant.DOMAIN_NAME_NOT_EXIST); + //检查当前域名是否被禁用 + //ParamCheckUtil.checkExpression(Objects.equals(EnableStatusEnum.ENABLE.getStatus(), domainNameDO.getEnable()), ExceptionConstant.DOMAIN_NAME_IS_DISABLE, domainNameDO.getDomain()); + //检查子域名是否重复 + ParamCheckUtil.checkExpression(!domainPortMappingMapper.checkRepeatBySubdomain(item.getSubdomain(), item.getDomainId(), null), ExceptionConstant.SUDOMAIN_NAME_CANNOT_REPEAT); + }); + } Date now = new Date(); PortMappingDO portMappingDO = new PortMappingDO(); portMappingDO.setLicenseId(req.getLicenseId()); portMappingDO.setProtocal(req.getProtocal()); - portMappingDO.setSubdomain(req.getSubdomain()); portMappingDO.setServerPort(req.getServerPort()); portMappingDO.setClientIp(req.getClientIp()); portMappingDO.setClientPort(req.getClientPort()); @@ -192,8 +197,18 @@ public class PortMappingService implements LifecycleBean { // 更新VisitorChannel visitorChannelService.addVisitorChannelByPortMapping(portMappingDO); // 更新域名映射 - if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { - ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); + if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && CollectionUtil.isNotEmpty(req.getDomainMappings())) { + req.getDomainMappings().forEach(item -> { + DomainNameDO domainNameDO = domainNameDOMap.get(item.getDomainId()); + //创建域名映射 + DomainPortMappingDO domainPortMappingDO = new DomainPortMappingDO(); + domainPortMappingDO.setDomainNameId(item.getDomainId()); + domainPortMappingDO.setSubdomain(item.getSubdomain()); + domainPortMappingDO.setPortMappingId(portMappingDO.getId()); + domainPortMappingMapper.insert(domainPortMappingDO); + //设置完整域名到服务端端口的映射 + ProxyUtil.setFullDomainToServerPort(StrUtil.join(StrPool.DOT, item.getSubdomain(), domainNameDO.getDomain()), portMappingDO.getServerPort()); + }); } updateMappingPortToSecurityGroupMap(portMappingDO.getServerPort(), req.getSecurityGroupId()); @@ -218,17 +233,26 @@ public class PortMappingService implements LifecycleBean { PortPoolDO portPoolDO = portPoolMapper.findByPort(req.getServerPort()); ParamCheckUtil.checkNotNull(portPoolDO, ExceptionConstant.PORT_NOT_EXIST); ParamCheckUtil.checkExpression(null == portMappingMapper.findByPort(req.getServerPort(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_CANNOT_REPEAT_MAPPING, req.getServerPort()); - ParamCheckUtil.checkExpression(!portMappingMapper.checkRepeatBySubdomain(req.getSubdomain(), Sets.newHashSet(req.getId())), ExceptionConstant.PORT_MAPPING_SUBDONAME_CONNOT_REPEAT); - // 查询原端口映射 PortMappingDO oldPortMappingDO = portMappingMapper.findById(req.getId()); ParamCheckUtil.checkNotNull(oldPortMappingDO, ExceptionConstant.PORT_MAPPING_NOT_EXIST); - + //验证域名映射相关参数条件 + Set domainIds = req.getDomainMappings().stream().map(item -> item.getDomainId()).collect(Collectors.toSet()); + List domainNameDOS = domainMapper.selectBatchIds(domainIds); + Map domainNameDOMap = domainNameDOS.stream().collect(Collectors.toMap(DomainNameDO::getId, Function.identity())); + if (NetworkProtocolEnum.isHttp(req.getProtocal()) && CollectionUtil.isNotEmpty(req.getDomainMappings())) { + req.getDomainMappings().forEach(item -> { + DomainNameDO domainNameDO = domainNameDOMap.get(item.getDomainId()); + ParamCheckUtil.checkNotNull(domainNameDO, ExceptionConstant.DOMAIN_NAME_NOT_EXIST); + //检查子域名是否重复 + ParamCheckUtil.checkExpression(!domainPortMappingMapper.checkRepeatBySubdomain(item.getSubdomain(), item.getDomainId(), req.getId()), + ExceptionConstant.SUDOMAIN_NAME_CANNOT_REPEAT); + }); + } // 更新端口映射 portMappingMapper.update(null, new LambdaUpdateWrapper() .eq(PortMappingDO::getId, req.getId()) .set(PortMappingDO::getProtocal, req.getProtocal()) - .set(PortMappingDO::getSubdomain, req.getSubdomain()) .set(PortMappingDO::getServerPort, req.getServerPort()) .set(PortMappingDO::getClientIp, req.getClientIp()) .set(PortMappingDO::getClientPort, req.getClientPort()) @@ -245,12 +269,27 @@ public class PortMappingService implements LifecycleBean { PortMappingDO portMappingDO = portMappingMapper.findById(req.getId()); visitorChannelService.updateVisitorChannelByPortMapping(oldPortMappingDO, portMappingDO); // 删除老的域名映射 - if (NetworkProtocolEnum.isHttp(oldPortMappingDO.getProtocal()) && StrUtil.isNotBlank(oldPortMappingDO.getSubdomain())) { - ProxyUtil.removeSubdomainToServerPort(oldPortMappingDO.getSubdomain()); + if (NetworkProtocolEnum.isHttp(oldPortMappingDO.getProtocal())) { + //删除完整域名到服务端端口的映射 + ProxyUtil.removeFullDomainToServerPortByServerPort(oldPortMappingDO.getServerPort()); + //删除旧的域名映射 + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery() + .eq(DomainPortMappingDO::getPortMappingId, oldPortMappingDO.getId()); + domainPortMappingMapper.delete(lambdaQueryWrapper); } // 更新域名映射 - if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(proxyConfig.getServer().getTcp().getDomainName()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { - ProxyUtil.setSubdomainToServerPort(portMappingDO.getSubdomain(), portMappingDO.getServerPort()); + if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && CollectionUtil.isNotEmpty(req.getDomainMappings())) { + req.getDomainMappings().forEach(item -> { + DomainNameDO domainNameDO = domainNameDOMap.get(item.getDomainId()); + //创建域名映射 + DomainPortMappingDO domainPortMappingDO = new DomainPortMappingDO(); + domainPortMappingDO.setDomainNameId(item.getDomainId()); + domainPortMappingDO.setSubdomain(item.getSubdomain()); + domainPortMappingDO.setPortMappingId(portMappingDO.getId()); + domainPortMappingMapper.insert(domainPortMappingDO); + //设置完整域名到服务端端口的映射 + ProxyUtil.setFullDomainToServerPort(StrUtil.join(StrPool.DOT, item.getSubdomain(), domainNameDO.getDomain()), portMappingDO.getServerPort()); + }); } updateMappingPortToSecurityGroupMap(portMappingDO.getServerPort(), req.getSecurityGroupId()); @@ -332,8 +371,13 @@ public class PortMappingService implements LifecycleBean { // 更新VisitorChannel visitorChannelService.removeVisitorChannelByPortMapping(portMappingDO); // 更新域名映射 - if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal()) && StrUtil.isNotBlank(portMappingDO.getSubdomain())) { - ProxyUtil.removeSubdomainToServerPort(portMappingDO.getSubdomain()); + if (NetworkProtocolEnum.isHttp(portMappingDO.getProtocal())) { + //删除完整域名到服务端端口的映射 + ProxyUtil.removeFullDomainToServerPortByServerPort(portMappingDO.getServerPort()); + //删除旧的域名映射 + LambdaQueryWrapper lambdaQueryWrapper = Wrappers.lambdaQuery() + .eq(DomainPortMappingDO::getPortMappingId, portMappingDO.getId()); + domainPortMappingMapper.delete(lambdaQueryWrapper); } updateMappingPortToSecurityGroupMap(portMappingDO.getServerPort(), null); @@ -406,24 +450,6 @@ public class PortMappingService implements LifecycleBean { // 刷新流量限制缓存 refreshFlowLimitCache(item.getId(), item.getUpLimitRate(), item.getDownLimitRate()); }); - - // 未配置域名,则不需要处理域名映射逻辑 - if (StrUtil.isBlank(proxyConfig.getServer().getTcp().getDomainName())) { - return; - } - List portMappingDOList = allMappingDOList.stream() - .filter(item -> NetworkProtocolEnum.HTTP.getDesc().equals(item.getProtocal()) && item.getSubdomain() != null) - .collect(Collectors.toList()); - if (CollectionUtil.isEmpty(portMappingDOList)) { - return; - } - portMappingDOList.forEach(item -> { - if (StrUtil.isBlank(item.getSubdomain())) { - return; - } - ProxyUtil.setSubdomainToServerPort(item.getSubdomain(), item.getServerPort()); - - }); } /** diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/bo/FullDomainNameBO.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/bo/FullDomainNameBO.java index 4e6e07c2..dbe597ed 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/bo/FullDomainNameBO.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/service/bo/FullDomainNameBO.java @@ -2,9 +2,7 @@ package org.dromara.neutrinoproxy.server.service.bo; import lombok.Data; import lombok.experimental.Accessors; -import org.dromara.neutrinoproxy.server.controller.res.report.HomeDataView; - -import java.util.Date; +import org.dromara.neutrinoproxy.server.constant.HttpsStatusEnum; /** * 完整域名 @@ -17,7 +15,7 @@ public class FullDomainNameBO { /** * 端口映射id */ - private Integer id; + private Integer portMappingId; /** * 子域名 */ @@ -30,5 +28,11 @@ public class FullDomainNameBO { /** * 域名id */ - private Integer domainId; + private Integer domainNameId; + + /** + * 强制使用HTTPS(1、是 2、否) + * {@link HttpsStatusEnum} + */ + private Integer forceHttps; } diff --git a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/util/ProxyUtil.java b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/util/ProxyUtil.java index 14ecd297..3a79815f 100644 --- a/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/util/ProxyUtil.java +++ b/neutrino-proxy-server/src/main/java/org/dromara/neutrinoproxy/server/util/ProxyUtil.java @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollectionUtil; import com.google.common.collect.Sets; import org.apache.commons.lang3.StringUtils; import org.dromara.neutrinoproxy.core.ChannelAttribute; -import org.dromara.neutrinoproxy.core.Constants; import org.dromara.neutrinoproxy.server.constant.NetworkProtocolEnum; import org.dromara.neutrinoproxy.server.proxy.domain.CmdChannelAttachInfo; import org.dromara.neutrinoproxy.server.proxy.domain.ProxyAttachment; @@ -19,6 +18,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.stream.Collectors; /** * @@ -32,7 +32,7 @@ public class ProxyUtil { */ private static final Map> licenseToServerPortMap = new HashMap<>(); /** - * 代理信息映射 + * 代理信息映射 e.g.: 9104 -> 127.0.0.1:8848 */ private static final Map proxyInfoMap = new ConcurrentHashMap<>(); /** @@ -61,9 +61,13 @@ public class ProxyUtil { */ private static Map proxyConnectAttachmentMap = new HashMap<>(); /** - * 子域名 - 服务端端口映射 + * 完整域名 - 服务端端口映射 */ - private static Map subdomainToServerPort = new HashMap<>(); + private static Map fullDomainToServerPortMap = new ConcurrentHashMap<>(); + /** + * 主域名 - 域名id映射 + */ + private static Map domainToDomainNameIdMap = new ConcurrentHashMap<>(); /** * licenseId - 客户端Id映射 */ @@ -339,32 +343,73 @@ public class ProxyUtil { } /** - * 设置子域名到服务端端口的映射 - * @param subdomain + * 设置完整域名到服务端端口的映射 + * @param fullDomain * @param serverPort */ - public static void setSubdomainToServerPort(String subdomain, Integer serverPort) { - subdomainToServerPort.put(subdomain, serverPort); + public static void setFullDomainToServerPort(String fullDomain, Integer serverPort) { + fullDomainToServerPortMap.put(fullDomain, serverPort); } /** - * 删除子域名到服务端端口的映射 - * @param subdomain + * 删除完整域名到服务端端口的映射 + * @param fullDomain */ - public static void removeSubdomainToServerPort(String subdomain) { - subdomainToServerPort.remove(subdomain); + public static void removeFullDomainToServerPort(String fullDomain) { + fullDomainToServerPortMap.remove(fullDomain); } + /** + * 通过服务器端口删除完整域名到服务端端口的映射 + * @param serverPort + */ + public static void removeFullDomainToServerPortByServerPort(Integer serverPort) { + fullDomainToServerPortMap.entrySet().removeIf(entry -> Objects.equals(entry.getValue(), serverPort)); + } + /** - * 根据子域名获取外网端口 - * @param subdomain + * 根据完整域名获取外网端口 + * @param fullDomain * @return */ - public static Integer getServerPortBySubdomain(String subdomain) { - return subdomainToServerPort.get(subdomain); + public static Integer getServerPortByFullDomain(String fullDomain) { + return fullDomainToServerPortMap.get(fullDomain); } - /** + /** + * 添加域名到域名id的映射 + */ + public static void setDomainToDomainNameId(String domain, Integer domainNameId) { + domainToDomainNameIdMap.put(domain, domainNameId); + } + + /** + * 删除域名到域名id的映射 + */ + public static void removeDomainToDomainNameId(String domain) { + domainToDomainNameIdMap.remove(domain); + } + + /** + * 通过主域名获取域名id + */ + public static Integer getDomainNameIdByDomain(String domain) { + return domainToDomainNameIdMap.get(domain); + } + + /** + * 通过完整域名获取域名id + */ + public static Integer getDomainNameIdByFullDomain(String fullDomain) { + List domains = domainToDomainNameIdMap.keySet().stream().filter(item -> fullDomain.endsWith(item)).collect(Collectors.toList()); + // 不存在 或者 有多条记录,返回null + if (CollectionUtil.isEmpty(domains) || domains.size() > 1) { + return null; + } + return getDomainNameIdByDomain(domains.get(0)); + } + + /** * 关闭http响应channel * @param channel * @return diff --git a/neutrino-proxy-server/src/main/resources/app.yml b/neutrino-proxy-server/src/main/resources/app.yml index c62aa9d0..cbdfa3a6 100644 --- a/neutrino-proxy-server/src/main/resources/app.yml +++ b/neutrino-proxy-server/src/main/resources/app.yml @@ -58,8 +58,6 @@ neutrino: http-proxy-port: ${HTTP_PROXY_PORT:80} # https代理端口,默认443 (需要配置域名、证书) https-proxy-port: ${HTTPS_PROXY_PORT:443} - # 如果不配置,则不支持域名映射 - domain-name: ${DOMAIN_NAME:} # https证书配置 key-store-password: ${HTTPS_STORE_PASS:} jks-path: ${HTTPS_JKS_PATH:} diff --git a/neutrino-proxy-server/src/main/resources/mapper/DomainMapper.xml b/neutrino-proxy-server/src/main/resources/mapper/DomainMapper.xml new file mode 100644 index 00000000..2500971a --- /dev/null +++ b/neutrino-proxy-server/src/main/resources/mapper/DomainMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/neutrino-proxy-server/src/main/resources/mapper/PortMappingMapper.xml b/neutrino-proxy-server/src/main/resources/mapper/PortMappingMapper.xml index cbac75ba..22348ef9 100644 --- a/neutrino-proxy-server/src/main/resources/mapper/PortMappingMapper.xml +++ b/neutrino-proxy-server/src/main/resources/mapper/PortMappingMapper.xml @@ -29,16 +29,4 @@ order by pm.id asc - - \ No newline at end of file