服务端、客户端认证逻辑调整.
This commit is contained in:
+2
-1
@@ -54,4 +54,5 @@ hs_err_pid*
|
||||
|
||||
**/memo/**
|
||||
|
||||
neutrino-proxy-vuepress/deploy.sh
|
||||
neutrino-proxy-vuepress/deploy.sh
|
||||
.NEUTRINO_PROXY_CLIENT_ID
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/**
|
||||
* 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.client.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author: aoshiguchen
|
||||
* @date: 2022/10/18
|
||||
*/
|
||||
@Data
|
||||
public class CustomConfig {
|
||||
private String jksPath;
|
||||
private String serverIp;
|
||||
private Integer serverPort;
|
||||
private Boolean sslEnable;
|
||||
private String licenseKey;
|
||||
}
|
||||
+1
@@ -39,5 +39,6 @@ public class ProxyConfig {
|
||||
private Integer obtainLicenseInterval;
|
||||
private String licenseKey;
|
||||
private Integer threadCount;
|
||||
private String clientId;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -142,7 +142,7 @@ public class ProxyClientService {
|
||||
log.error("client start error", e);
|
||||
}
|
||||
} else {
|
||||
channel.writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getClient().getLicenseKey()));
|
||||
channel.writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getClient().getLicenseKey(), ProxyUtil.getClientId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ public class ProxyClientService {
|
||||
channel = future.channel();
|
||||
// 连接成功,向服务器发送客户端认证信息(licenseKey)
|
||||
ProxyUtil.setCmdChannel(future.channel());
|
||||
future.channel().writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getClient().getLicenseKey()));
|
||||
future.channel().writeAndFlush(ProxyMessage.buildAuthMessage(proxyConfig.getClient().getLicenseKey(), ProxyUtil.getClientId()));
|
||||
log.info("连接代理服务成功. channelId:{}", future.channel().id().asLongText());
|
||||
|
||||
// reconnectServiceEnable = true;
|
||||
|
||||
+3
-1
@@ -31,7 +31,9 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
log.info("client auth failed , client stop.");
|
||||
context.channel().close();
|
||||
Solon.stop();
|
||||
} else {
|
||||
} else if (ExceptionEnum.CONNECT_FAILED.getCode().equals(code) ||
|
||||
ExceptionEnum.LICENSE_CANNOT_REPEAT_CONNECT.getCode().equals(code)
|
||||
){
|
||||
context.channel().close();
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -21,6 +21,8 @@
|
||||
*/
|
||||
package org.dromara.neutrinoproxy.client.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.dromara.neutrinoproxy.client.config.ProxyConfig;
|
||||
import org.dromara.neutrinoproxy.client.core.ProxyChannelBorrowListener;
|
||||
import org.dromara.neutrinoproxy.core.Constants;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
@@ -29,10 +31,12 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.util.AttributeKey;
|
||||
import org.dromara.neutrinoproxy.core.util.FileUtil;
|
||||
import org.noear.solon.Solon;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
@@ -54,6 +58,9 @@ public class ProxyUtil {
|
||||
|
||||
private static volatile Channel cmdChannel;
|
||||
|
||||
private static String clientId;
|
||||
private static final String CLIENT_ID_FILE = ".NEUTRINO_PROXY_CLIENT_ID";
|
||||
|
||||
public static void borrowProxyChanel(Bootstrap bootstrap, final ProxyChannelBorrowListener borrowListener) {
|
||||
Channel channel = proxyChannelPool.poll();
|
||||
if (null != channel) {
|
||||
@@ -129,4 +136,24 @@ public class ProxyUtil {
|
||||
|
||||
realServerChannels.clear();
|
||||
}
|
||||
|
||||
public static String getClientId() {
|
||||
if (StringUtils.isNotBlank(clientId)) {
|
||||
return clientId;
|
||||
}
|
||||
ProxyConfig proxyConfig = Solon.context().getBean(ProxyConfig.class);
|
||||
if (StringUtils.isNotBlank(proxyConfig.getClient().getClientId())) {
|
||||
clientId = proxyConfig.getClient().getClientId();
|
||||
return clientId;
|
||||
}
|
||||
String id = FileUtil.readContentAsString(CLIENT_ID_FILE);
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
clientId = id;
|
||||
return id;
|
||||
}
|
||||
id = UUID.randomUUID().toString().replace("-", "");
|
||||
FileUtil.write(CLIENT_ID_FILE, id);
|
||||
clientId = id;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,3 +21,4 @@ neutrino:
|
||||
ssl-enable: ${SSL_ENABLE:true}
|
||||
obtain-license-interval: 5
|
||||
license-key: ${LICENSE_KEY:}
|
||||
client-id: ${CLIENT_ID:}
|
||||
|
||||
@@ -36,6 +36,7 @@ public enum ExceptionEnum {
|
||||
SUCCESS(0, "成功"),
|
||||
AUTH_FAILED(1, "认证失败"),
|
||||
CONNECT_FAILED(2, "连接失败"),
|
||||
@Deprecated
|
||||
LICENSE_CANNOT_REPEAT_CONNECT(3, "license不能多个客户端同时使用"),
|
||||
;
|
||||
|
||||
|
||||
@@ -100,9 +100,9 @@ public class ProxyMessage {
|
||||
return create().setType(TYPE_HEARTBEAT);
|
||||
}
|
||||
|
||||
public static ProxyMessage buildAuthMessage(String info) {
|
||||
public static ProxyMessage buildAuthMessage(String info, String clientId) {
|
||||
return create().setType(TYPE_AUTH)
|
||||
.setInfo(info);
|
||||
.setInfo(info + "," + clientId);
|
||||
}
|
||||
|
||||
public static ProxyMessage buildAuthResultMessage(Integer code, String msg, String licenseKey) {
|
||||
|
||||
+2
@@ -99,6 +99,8 @@ public class ProxyTunnelChannelHandler extends SimpleChannelInboundHandler<Proxy
|
||||
.setCreateTime(new Date())
|
||||
);
|
||||
ProxyUtil.removeCmdChannel(ctx.channel());
|
||||
// 防止下次换一个客户端,无法连接的情况
|
||||
ProxyUtil.removeClientIdByLicenseId(cmdChannelAttachInfo.getLicenseId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-12
@@ -79,8 +79,17 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
String ip = ((InetSocketAddress)ctx.channel().remoteAddress()).getAddress().getHostAddress();
|
||||
Date now = new Date();
|
||||
|
||||
String info = proxyMessage.getInfo();
|
||||
String[] tmp = info.split(",");
|
||||
String licenseKey = proxyMessage.getInfo();
|
||||
String clientId = "";
|
||||
if (tmp.length == 2) {
|
||||
licenseKey = tmp[0];
|
||||
clientId = tmp[1];
|
||||
}
|
||||
|
||||
if (StrUtil.isEmpty(licenseKey)) {
|
||||
log.warn("[客户端连接] license不能为空 info:{} ", info);
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "license不能为空!", licenseKey));
|
||||
ctx.channel().close();
|
||||
clientConnectRecordService.add(new ClientConnectRecordDO()
|
||||
@@ -95,6 +104,7 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
}
|
||||
LicenseDO licenseDO = licenseService.findByKey(licenseKey);
|
||||
if (null == licenseDO) {
|
||||
log.warn("[客户端连接] license不存在 info:{} ", info);
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "license不存在!", licenseKey));
|
||||
ctx.channel().close();
|
||||
clientConnectRecordService.add(new ClientConnectRecordDO()
|
||||
@@ -108,6 +118,7 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
return;
|
||||
}
|
||||
if (EnableStatusEnum.DISABLE.getStatus().equals(licenseDO.getEnable())) {
|
||||
log.warn("[客户端连接] 当前license已被禁用 info:{} ", info);
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "当前license已被禁用!", licenseKey));
|
||||
ctx.channel().close();
|
||||
clientConnectRecordService.add(new ClientConnectRecordDO()
|
||||
@@ -122,6 +133,7 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
}
|
||||
UserDO userDO = userService.findById(licenseDO.getUserId());
|
||||
if (null == userDO || EnableStatusEnum.DISABLE.getStatus().equals(userDO.getEnable())) {
|
||||
log.warn("[客户端连接] 当前license无效 info:{} ", info);
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "当前license无效!", licenseKey));
|
||||
ctx.channel().close();
|
||||
clientConnectRecordService.add(new ClientConnectRecordDO()
|
||||
@@ -136,17 +148,21 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
}
|
||||
Channel cmdChannel = ProxyUtil.getCmdChannelByLicenseId(licenseDO.getId());
|
||||
if (null != cmdChannel) {
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.LICENSE_CANNOT_REPEAT_CONNECT.getCode(), "当前license已被另一节点使用!", licenseKey));
|
||||
ctx.channel().close();
|
||||
clientConnectRecordService.add(new ClientConnectRecordDO()
|
||||
.setIp(ip)
|
||||
.setLicenseId(licenseDO.getId())
|
||||
.setType(ClientConnectTypeEnum.CONNECT.getType())
|
||||
.setMsg(licenseKey)
|
||||
.setCode(SuccessCodeEnum.FAIL.getCode())
|
||||
.setErr("当前license已被另一节点使用!")
|
||||
.setCreateTime(now));
|
||||
return;
|
||||
String _clientId = ProxyUtil.getClientIdByLicenseId(licenseDO.getId());
|
||||
if (!clientId.equals(_clientId)) {
|
||||
log.warn("[客户端连接] 当前license已被另一节点使用 info:{} ", info);
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.AUTH_FAILED.getCode(), "当前license已被另一节点使用!", licenseKey));
|
||||
ctx.channel().close();
|
||||
clientConnectRecordService.add(new ClientConnectRecordDO()
|
||||
.setIp(ip)
|
||||
.setLicenseId(licenseDO.getId())
|
||||
.setType(ClientConnectTypeEnum.CONNECT.getType())
|
||||
.setMsg(licenseKey)
|
||||
.setCode(SuccessCodeEnum.FAIL.getCode())
|
||||
.setErr("当前license已被另一节点使用!")
|
||||
.setCreateTime(now));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 发送认证成功消息
|
||||
ctx.channel().writeAndFlush(ProxyMessage.buildAuthResultMessage(ExceptionEnum.SUCCESS.getCode(), "认证成功!", licenseKey));
|
||||
@@ -155,10 +171,15 @@ public class ProxyMessageAuthHandler implements ProxyMessageHandler {
|
||||
.setIp(ip)
|
||||
.setLicenseId(licenseDO.getId())
|
||||
.setType(ClientConnectTypeEnum.CONNECT.getType())
|
||||
.setMsg(licenseKey)
|
||||
.setMsg(info)
|
||||
.setCode(SuccessCodeEnum.SUCCESS.getCode())
|
||||
.setCreateTime(now));
|
||||
|
||||
// 设置当前licenseId对应的客户端ID
|
||||
ProxyUtil.setLicenseIdToClientIdMap(licenseDO.getId(), clientId);
|
||||
|
||||
log.warn("[客户端连接] 认证成功 info:{} ", info);
|
||||
|
||||
// 更新license在线状态
|
||||
licenseMapper.updateOnlineStatus(licenseDO.getId(), OnlineStatusEnum.ONLINE.getStatus(), now);
|
||||
// 初始化VisitorChannel
|
||||
|
||||
+30
@@ -63,6 +63,10 @@ public class ProxyUtil {
|
||||
* 子域名 - 服务端端口映射
|
||||
*/
|
||||
private static Map<String, Integer> subdomainToServerPort = new HashMap<>();
|
||||
/**
|
||||
* licenseId - 客户端Id映射
|
||||
*/
|
||||
private static Map<Integer, String> licenseIdToClientIdMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 初始化代理信息
|
||||
@@ -387,4 +391,30 @@ public class ProxyUtil {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置licenseId - clientId映射
|
||||
* @param licenseId
|
||||
* @param clientId
|
||||
*/
|
||||
public static void setLicenseIdToClientIdMap(Integer licenseId, String clientId) {
|
||||
licenseIdToClientIdMap.put(licenseId, clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据licenseId获取clientId
|
||||
* @param licenseId
|
||||
* @return
|
||||
*/
|
||||
public static String getClientIdByLicenseId(Integer licenseId) {
|
||||
return licenseIdToClientIdMap.get(licenseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据licenseId删除clientId
|
||||
* @param licenseId
|
||||
*/
|
||||
public static void removeClientIdByLicenseId(Integer licenseId) {
|
||||
licenseIdToClientIdMap.remove(licenseId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user