当前位置: 首页 > 知识库问答 >
问题:

Netty ProxyHandler writeAndFlush未向服务器写入响应

范麒
2023-03-14

我试图在Netty中实现一个NTLMProxyHandler,该NTLMProxyHandler可以执行NTLM消息交换,并通过web代理对客户端进行身份验证。

NTLMProxyHandler扩展了Netty的ProxyHandler类。因此,代理处理程序会触发一个初始的HTTP请求,并到达我创建的模拟代理服务器。代理服务器读取此请求,并以407代理身份验证所需的响应进行响应。

我在日志中启用了Netty包,但无法弄清楚为什么只有第二次从ntlm代理处理程序写入的响应丢失。

我尝试使用Netty ProxyHandler的sendToProxyServer(msg)以及从channelRead()传递的channelHandlerCtx。在这两种情况下,writeAndFlush都完成了,但是响应从未到达服务器,服务器超时。

是否有人使用channelHandlerCtx向服务器写回响应并执行类似于此的消息交换?

    null

任何指针都会很有帮助。谢了!

ntlmproxyhandler.java

    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelPipeline;
    import io.netty.handler.codec.http.DefaultFullHttpRequest;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.FullHttpResponse;
    import io.netty.handler.codec.http.HttpClientCodec;
    import io.netty.handler.codec.http.HttpContent;
    import io.netty.handler.codec.http.HttpHeaderNames;
    import io.netty.handler.codec.http.HttpHeaders;
    import io.netty.handler.codec.http.HttpMethod;
    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.LastHttpContent;
    import io.netty.handler.proxy.ProxyConnectException;
    import jcifs.ntlmssp.Type1Message;
    import jcifs.ntlmssp.Type2Message;
    import jcifs.ntlmssp.Type3Message;
    import jcifs.smb.NtlmContext;
    import jcifs.smb.NtlmPasswordAuthentication;
    import jcifs.util.Base64;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import java.net.InetSocketAddress;
    import java.net.SocketAddress;


    public class NTLMProxyHandler extends AbstractProxyHandler {

        private String userName;
        private String password;
        private final static String DOMAIN      = "CORP";
        public static final String NTLM_Prefix = "NTLM";

        private static final Logger logger = LoggerFactory.getLogger(NTLMProxyHandler.class);

        private static int NTLMV2_FLAGS_TYPE3 = 0xa2888205;
        private HttpResponseStatus status;
        private HttpResponse response;

        private NtlmPasswordAuthentication ntlmPasswordAuthentication;
        private NtlmContext ntlmContext;
        private final HttpClientCodec codec = new HttpClientCodec();

        public NTLMProxyHandler(SocketAddress proxyAddress) {
            super(proxyAddress);
        }

        public NTLMProxyHandler(SocketAddress proxyAddress, String domain, String username, String password) {
            super(proxyAddress);
            setConnectTimeoutMillis(50000);
            this.userName = username;
            this.password = password;
            ntlmPasswordAuthentication = new NtlmPasswordAuthentication(DOMAIN, username, password);
            ntlmContext = new NtlmContext(ntlmPasswordAuthentication, true);
        }

        @Override
        public String protocol() {
            return "http";
        }

        @Override
        public String authScheme() {
            return "ntlm";
        }

        protected void addCodec(ChannelHandlerContext ctx) throws Exception {
            ChannelPipeline p = ctx.pipeline();
            String name = ctx.name();
            p.addBefore(name, (String)null, this.codec);
        }

        protected void removeEncoder(ChannelHandlerContext ctx) throws Exception {
            this.codec.removeOutboundHandler();
        }

        protected void removeDecoder(ChannelHandlerContext ctx) throws Exception {
            this.codec.removeInboundHandler();
        }

        @Override
        protected Object newInitialMessage(ChannelHandlerContext channelHandlerContext) throws Exception {
            InetSocketAddress raddr = this.destinationAddress();
            String rhost;
            if(raddr.isUnresolved()) {
                rhost = raddr.getHostString();
            } else {
                rhost = raddr.getAddress().getHostAddress();
            }

            String host = rhost + ':' + raddr.getPort();
            DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, host, Unpooled.EMPTY_BUFFER, false);
            req.headers().set(HttpHeaderNames.HOST, host);
            req.headers().set("connection", "keep-alive");

// This initial request successfully reaches the server !
            return req;
        }

        @Override
        protected boolean handleResponse(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {

            if (o instanceof HttpResponse) {
                 response = (HttpResponse) o;

            }
            boolean finished = o instanceof LastHttpContent;

            if(finished) {
                status = response.status();
                logger.info("Status: " + status);

                if (!response.headers().isEmpty()) {
                    for (String name: response.headers().names()) {
                        for (String value: response.headers().getAll(name)) {
                            logger.debug("Header: " + name + " = " + value);
                        }
                    }
                }
                if(status.code() == 407) {
                    negotiate(channelHandlerContext, response);
                }
                else if(status.code() == 200){
                    logger.info("Client: NTLM exchange complete. Authenticated !");
                }
                else {
                    throw new ProxyConnectException(this.exceptionMessage("status: " + this.status));
                }
            }

            return finished;
        }

        private void negotiate(ChannelHandlerContext channelHandlerContext, HttpResponse msg) throws Exception{
            String ntlmHeader = msg.headers().get(HttpHeaderNames.PROXY_AUTHENTICATE);

            if(ntlmHeader.equalsIgnoreCase("NTLM")){
                logger.info("Client: Creating NTLM Type1Message");
                //Send Type1Message
                byte[] rawType1Message = ntlmContext.initSecContext(new byte[]{}, 0, 0);
                Type1Message type1Message = new Type1Message(rawType1Message);

                FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
                String proxyAuthHeader = Base64.encode(type1Message.toByteArray());
                logger.info("Setting proxyAuthHeader = " + proxyAuthHeader);
                response.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, proxyAuthHeader);

                ByteBuf byteBuf = Unpooled.buffer(rawType1Message.length);
                byteBuf.writeBytes(response.content());

//This is where the response is lost and never reaches the proxy server
                sendToProxyServer(byteBuf);
                // channelHandlerContext.writeAndFlush(response.content));

            } else if (ntlmHeader.contains(NTLM_Prefix)) {
                logger.info("Client: Creating NTLM Type3Message");
                //Send Type3 Message

            }
        }
    }

共有1个答案

阎德辉
2023-03-14

我终于想出了问题。NTLM代理处理程序在响应代理消息时发送的是FullHTTPResponse而不是FullHttpRequest。看起来Netty的管道丢弃了作为响应写入的数据,日志中没有指出这一点。

DefaultFullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, host, Unpooled.EMPTY_BUFFER, false);
req.headers().set(HttpHeaderNames.HOST, host);
req.headers().set(HttpHeaders.Names.PROXY_AUTHORIZATION, "type3message");

sendToProxyServer(req);
 类似资料:
  • 当我运行服务器时。在同一台hazelcast客户端中,我可以看到我能够在同一台物理计算机上使用两个成员组成集群。但是,当我尝试使用以下代码使用相同的hazelcast生成一个新实例(在同一台物理机器上)时。xml配置文件(作为示例客户机正在使用的文件),我的服务器实例似乎没有加入samle客户机节点。我正在启动的服务器进程无法启动(我可以在visualvm中看到它)。我已经加入了- 服务器示例:

  • 我知道有很多关于这个的问题,但是我确实经历了所有的问题,并且有点困惑自己,我列出了我遵循的步骤,请让我知道我把它搞砸了。 1) 我只想在应用程序级别使用Log4j,所以需要复制WL_HOME/server/lib/wllog4j。jar和log4j。jar在Domail_Home/Lib? 2) 我使用的是Maven,我在pom中添加了Log4j依赖项。xml[war]。我把战争藏在耳朵里。 3)

  • Response响应对象主要将JSP容器处理后的结果传回到客户端。可以通过response变量设置HTTP的状态和向客户端发送数据,如Cookie、HTTP文件头信息等。 一个典型的响应看起来就像下面这样: HTTP/1.1 200 OK Content-Type: text/html Header2: ... ... HeaderN: ... (空行) <!doctype ...> <ht

  • 托管在EC2中的我的服务(myservice.com)已经启动并运行。我可以看到java进程在机器内运行,但无法从外部机器访问服务。尝试了以下选项, dns short myservice。com ping myservice。com公司 (1) 正在解析并给我ip地址<代码>ping导致100%的数据包丢失。无法访问服务。 不知道去哪里看。一些调试帮助会很有帮助。 编辑: 我之前的部署有一个问题

  • 问题 你想通过网络提供持续的服务,与客户保持持续的联系。 解决方案 创建一个双向 TCP 服务器。 在 Node.js 中 net = require 'net' domain = 'localhost' port = 9001 server = net.createServer (socket) -> console.log "New connection from #{socket

  • 问题内容: 我在DWR中使用Spring。我想返回一个文件对象作为响应,但是我将文件(要发送)保存在服务器的临时位置,然后将其位置作为href发送给客户端的锚标记,但是我想知道是否有办法抛出该文件文件直接发送到响应对象上的浏览器,而无需将其临时保存在服务器上。 我希望是否有办法通过DWR发送文件作为响应。 问题答案: