support username/password in url; websocket support url with parameters. #1655 #1727

This commit is contained in:
hengyunabc
2021-03-05 17:19:28 +08:00
parent 082672fb1a
commit 51bf98cdc5
8 changed files with 49 additions and 15 deletions
@@ -25,6 +25,7 @@ public class ArthasConstants {
public static final int TELNET_PORT = 3658;
public static final String DEFAULT_WEBSOCKET_PATH = "/ws";
public static final int WEBSOCKET_IDLE_SECONDS = 60;
/**
@@ -35,4 +36,6 @@ public class ArthasConstants {
public static final String DEFAULT_USERNAME = "arthas";
public static final String SUBJECT_KEY = "subject";
public static final String AUTH = "auth";
public static final String USERNAME_KEY = "username";
public static final String PASSWORD_KEY = "password";
}
@@ -1,6 +1,8 @@
package com.taobao.arthas.core.shell.term.impl.http;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;
import javax.security.auth.Subject;
@@ -26,6 +28,7 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.Attribute;
/**
@@ -61,9 +64,13 @@ public final class BasicHttpAuthenticatorHandler extends ChannelDuplexHandler {
authed = true;
}
// 判断请求header里是否带有 username/password
if (!authed) {
// 判断请求header里是否带有 username/password
BasicPrincipal principal = extractBasicAuthSubject(httpRequest);
if (principal == null) {
// 判断 url里是否有 username/password
principal = extractBasicAuthSubjectFromUrl(httpRequest);
}
Subject subject = securityAuthenticator.login(principal);
if (subject != null) {
authed = true;
@@ -103,6 +110,32 @@ public final class BasicHttpAuthenticatorHandler extends ChannelDuplexHandler {
super.write(ctx, msg, promise);
}
/**
* 从url参数里提取 ?username=hello&password=world
*
* @param request
* @return
*/
protected static BasicPrincipal extractBasicAuthSubjectFromUrl(HttpRequest request) {
QueryStringDecoder queryDecoder = new QueryStringDecoder(request.uri());
Map<String, List<String>> parameters = queryDecoder.parameters();
List<String> passwords = parameters.get(ArthasConstants.PASSWORD_KEY);
if (passwords == null || passwords.size() == 0) {
return null;
}
String password = passwords.get(0);
String username = ArthasConstants.DEFAULT_USERNAME;
List<String> usernames = parameters.get(ArthasConstants.USERNAME_KEY);
if (usernames != null && !usernames.isEmpty()) {
username = usernames.get(0);
}
BasicPrincipal principal = new BasicPrincipal(username, password);
logger.debug("Extracted Basic Auth principal from url: {}", principal);
return principal;
}
/**
* Extracts the username and password details from the HTTP basic header
* Authorization.
@@ -59,7 +59,8 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequ
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (wsUri.equalsIgnoreCase(request.uri())) {
String path = new URI(request.uri()).getPath();
if (wsUri.equalsIgnoreCase(path)) {
ctx.fireChannelRead(request.retain());
} else {
if (HttpUtil.is100ContinueExpected(request)) {
@@ -67,7 +68,6 @@ public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequ
}
HttpResponse response = null;
String path = new URI(request.uri()).getPath();
if ("/".equals(path)) {
path = "/index.html";
}
@@ -13,8 +13,6 @@ import io.netty.util.concurrent.EventExecutorGroup;
import io.termd.core.function.Consumer;
import io.termd.core.tty.TtyConnection;
import java.io.File;
import com.taobao.arthas.common.ArthasConstants;
/**
@@ -42,8 +40,8 @@ public class LocalTtyServerInitializer extends ChannelInitializer<LocalChannel>
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH));
pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws"));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH));
pipeline.addLast(new WebSocketServerProtocolHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH, true));
pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS));
pipeline.addLast(new TtyWebSocketFrameHandler(group, handler));
}
@@ -42,8 +42,8 @@ public class TtyServerInitializer extends ChannelInitializer<SocketChannel> {
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH));
pipeline.addLast(new BasicHttpAuthenticatorHandler(httpSessionManager));
pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws"));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH));
pipeline.addLast(new WebSocketServerProtocolHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH, true));
pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS));
pipeline.addLast(new TtyWebSocketFrameHandler(group, handler));
}
@@ -93,8 +93,8 @@ public class ProtocolDetectHandler extends ChannelInboundHandlerAdapter {
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(ArthasConstants.MAX_HTTP_CONTENT_LENGTH));
pipeline.addLast(new BasicHttpAuthenticatorHandler(httpSessionManager));
pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler("/ws"));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
pipeline.addLast(workerGroup, "HttpRequestHandler", new HttpRequestHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH));
pipeline.addLast(new WebSocketServerProtocolHandler(ArthasConstants.DEFAULT_WEBSOCKET_PATH, true));
pipeline.addLast(new IdleStateHandler(0, 0, ArthasConstants.WEBSOCKET_IDLE_SECONDS));
pipeline.addLast(new TtyWebSocketFrameHandler(channelGroup, ttyConnectionFactory));
ctx.fireChannelActive();
@@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory;
import com.alibaba.arthas.tunnel.common.SimpleHttpResponse;
import com.alibaba.arthas.tunnel.server.cluster.TunnelClusterStore;
import com.taobao.arthas.common.ArthasConstants;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
@@ -33,12 +34,11 @@ import io.netty.util.concurrent.Promise;
*/
public class TunnelServer {
private final static Logger logger = LoggerFactory.getLogger(TunnelServer.class);
public static final String DEFAULT_WEBSOCKET_PATH = "/ws";
private boolean ssl;
private String host;
private int port;
private String path = DEFAULT_WEBSOCKET_PATH;
private String path = ArthasConstants.DEFAULT_WEBSOCKET_PATH;
private Map<String, AgentInfo> agentInfoMap = new ConcurrentHashMap<String, AgentInfo>();
@@ -6,8 +6,8 @@ import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import com.alibaba.arthas.tunnel.server.TunnelServer;
import com.alibaba.arthas.tunnel.server.utils.InetAddressUtil;
import com.taobao.arthas.common.ArthasConstants;
/**
*
@@ -58,7 +58,7 @@ public class ArthasProperties {
private String host;
private int port;
private boolean ssl;
private String path = TunnelServer.DEFAULT_WEBSOCKET_PATH;
private String path = ArthasConstants.DEFAULT_WEBSOCKET_PATH;
/**
* 客户端连接的地址。也用于保存到redis里,当部署tunnel server集群里需要。不配置则会自动获取