嵌入式Jetty:在安全的https服务器中,ContextHandler重定向到http URI [英] Embedded Jetty: In secure https server, ContextHandler redirects to http URI

查看:111
本文介绍了嵌入式Jetty:在安全的https服务器中,ContextHandler重定向到http URI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用嵌入式Jetty构建沙盒RESTful API.我的概念验证设计:一个简单的嵌入式码头服务器,该服务器(1)接受SSL端口上的连接,(2)使用ContextHandlerCollection来基于URI前缀调用适当的处理程序.

I'm building a sandbox RESTful API using embedded Jetty. My Proof-of-Concept design: a simple embedded jetty server that (1) accepts connections on an SSL port and (2) uses a ContextHandlerCollection to invoke the proper Handler based on the URI prefix.

我的原始测试使用一个简单的非SSL连接,似乎运行良好(请注意,用于导入的代码和APPENDIX中的帮助器HelloHandler类).

My original test, using a simple non-SSL connection, seemed to work perfectly (note, code for imports and helper HelloHandler class in APPENDIX).

public static void main(String[] args) throws Exception {
    Server server = new Server(12000); 

    ContextHandler test1Context = new ContextHandler();
    test1Context.setContextPath("/test1");
    test1Context.setHandler(new HelloHandler("Hello1"));

    ContextHandler test2Context = new ContextHandler();
    test2Context.setContextPath("/test2");
    test2Context.setHandler(new HelloHandler("Hello2"));

    ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
    contextHandlers.setHandlers(new Handler[] { test1Context, test2Context });

    server.setHandler(contextHandlers);
    server.start();
    server.join();
}

但是,在测试此代码时,我忽略了当我省略尾随的前斜杠时浏览器重定向发生的情况,因此http://localhost:12000/test1被重定向到了http://localhost:12000/test1/. (FWIW,该异地站点稍后将转化为4个小时以上的故障排除时间).

However, while testing this, it escaped my attention that browser redirects were happening when I omitted a trailing forward slash, so http://localhost:12000/test1 was getting redirected to http://localhost:12000/test1/. (FWIW, that oversite would later translate to 4+ hours of troubleshooting).

当我切换到HTTPS SSL连接时,一切都出错了.下面的代码:

When I switched to an HTTPS SSL connection, everything went wrong. Code below:

public static void main(String[] args) throws Exception {
    Server server = new Server(); 

    // Setups
    SslContextFactory sslContextFactory = new SslContextFactory();
    sslContextFactory.setKeyStorePath("C:/keystore.jks");
    sslContextFactory.setKeyStorePassword("password");
    sslContextFactory.setKeyManagerPassword("password");

    ContextHandler test1Context = new ContextHandler();
    test1Context.setContextPath("/test1");
    test1Context.setHandler(new HelloHandler("Hello1"));

    ContextHandler test2Context = new ContextHandler();
    test2Context.setContextPath("/test2");
    test2Context.setHandler(new HelloHandler("Hello2"));

    ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
    contextHandlers.setHandlers(new Handler[] { test1Context, test2Context });

    ServerConnector serverConnector = new ServerConnector(server,
            new SslConnectionFactory(sslContextFactory, "http/1.1"),
            new HttpConnectionFactory());

    serverConnector.setPort(12000);
    server.setConnectors(new Connector[] { serverConnector });
    server.setHandler(contextHandlers);

    server.start();
    server.join();
}

症状:

尝试使用https://localhost:12000/test1(不带斜杠)将导致浏览器报告服务器已重置连接.此外,最初我发现 not 的操作是将URI重定向到http://localhost:12000/test1/(不是https!).有趣的是(以一种幽默的幽默感),我偶尔会在代码中更改一些无关紧要的内容,然后不经意地使用https://localhost:12000/test1/进行测试,它将起作用.言语不能弥补这种假阳性所带来的挫败感.

Attempting to use https://localhost:12000/test1 (no trailing slash) would cause browser to report that the server had reset the connection. Additionally, and what I did not spot initially, the URI was being redirected to http://localhost:12000/test1/ (not https!). Amusingly (in a sadistic-sense-of-humor sort of way), on a couple occasions, I'd change something inconsequential in the code and then inadvertently test with https://localhost:12000/test1/ and it would work. Words don't do justice to the frustration such false-positives caused.

除了浏览器重定向并报告连接重置错误外,我的服务器日志中还会出现以下异常:

In addition to the browser redirecting and reporting a connection-reset error, I would get the following exception in my server logs:

2013-11-23 13:57:48 DEBUG org.eclipse.jetty.server.HttpConnection:282 - 
org.eclipse.jetty.io.EofException
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:653)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:240)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
    at java.lang.Thread.run(Thread.java:722)
Caused by: javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
    at sun.security.ssl.EngineInputRecord.bytesInCompletePacket(EngineInputRecord.java:171)
    at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:845)
    at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:758)
    at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
    at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.fill(SslConnection.java:518)
    ... 5 more

不幸的是,当关键线索在浏览器重定向的URL中时,我花费了所有时间尝试直接解决此异常.事实证明ContextHandler代码具有默认行为,当不存在尾部正斜杠时,该行为将导致其仅重定向到具有尾部斜杠的URI.不幸的是,此重定向是到HTTP URI的,因此HTTPS方案被丢弃,这导致服务器抱怨纯文本.

Unfortunately, I spent all of my time trying to troubleshoot this exception directly, when the key clue was in the browser redirected URL. It turns out that the ContextHandler code has a default behavior which, when no trailing forward slash exists, causes it to just redirect to a URI that has the trailing slash. Unfortunately, this redirect is to an HTTP URI - so the HTTPS scheme is dropped, which caused the server to complain about the plain-text.

担心:

一旦我知道了这种重定向行为,谷歌对那个实际问题的快速搜索就导致我找到ContextHandler.setAllowNullPathInfo(true)方法-这将关闭此重定向行为.在上面的代码中,这是用两行代码完成的:

Once this redirect behavior became clear to me, a quick Google search of that actual issue led me to the ContextHandler.setAllowNullPathInfo(true) method - which turns off this redirect behavior. In my code above, this is accomplished with 2 lines:

test1Context.setAllowNullPathInfo(true);
test2Context.setAllowNullPathInfo(true);

此职位的主要观点:

我花了3-4个小时来尝试解决"javax.net.ssl.SSLException:无法识别的SSL消息,纯文本连接?"异常,并且在网上都找不到与上述解决方案/解决方案相关联的异常.如果我从经历的挫折中拯救了另一个开发人员,那么任务就完成了.

I spent 3 - 4 hours trying to troubleshoot the "javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?" exception, and nowhere on the web did I find that exception linked to the solution/workaround above. If I save even one other developer from the frustration I experienced, then mission accomplished.

挥之不去的问题(发布此问题的其他原因): 好的,所以我使这段代码起作用了,但是不得不承认:我非常难以接受的事实是,我做的非常简单的概念验证测试(我认为这很普遍)遇到了这种情况,这似乎是史无前例的.这就告诉我,我可能正在做一些超出最佳实践"范围的事情.或者,更糟糕的是,在设计此方法时完全犯了错误.所以,问题:

LINGERING QUESTIONS (OTHER REASON FOR POSTING THIS): Okay, so I got this code working, but have to confess: I'm incredibly uneasy about the fact that my incredibly simple proof-of-concept test, doing something that I assume would be quite common, encountered this situation which seems utterly unprecedented. That tells me, I'm probably doing something beyond the realm of "best practice"; or, worse, doing something utterly wrong in how I'm designing this. So, questions:

1)我做错了吗?

2)为什么ContextHandler的默认行为是重定向缺少尾随空间的URI?通过setAllowNullPathInfo(true)覆盖默认行为,会带来什么风险?

2) Why is the default behavior of ContextHandler to redirect URI's which lack trailing space? What risk(s) am I incurring by over-riding that default behavior with setAllowNullPathInfo(true)?

附录(帮助程序类和导入的代码) 进口: 导入java.io.IOException;

APPENDIX (code of helper class and imports) Imports: import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;

助手类:

static class HelloHandler extends AbstractHandler {
    final String _greeting;

    public HelloHandler(String greeting) {
        _greeting = greeting;
    }

    public void handle(String target, Request baseRequest,
            HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        baseRequest.setHandled(true);
        response.getWriter().println("<h1>" + _greeting + "</h1>");
    }
}

推荐答案

您缺少HttpConfiguration设置.

You are missing the HttpConfiguration setup.

这里...

package jetty.examples;

import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.util.ssl.SslContextFactory;

public class SecureContexts
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server();
        int port = 12000;

        // Setup SSL
        SslContextFactory sslContextFactory = new SslContextFactory();
        sslContextFactory.setKeyStorePath(System.getProperty("jetty.keystore.path","C:/keystore.jks"));
        sslContextFactory.setKeyStorePassword(System.getProperty("jetty.keystore.password","password"));
        sslContextFactory.setKeyManagerPassword(System.getProperty("jetty.keymanager.password","password"));

        // Setup HTTP Configuration
        HttpConfiguration httpConf = new HttpConfiguration();
        httpConf.setSecurePort(port);
        httpConf.setSecureScheme("https");
        httpConf.addCustomizer(new SecureRequestCustomizer());

        ContextHandler test1Context = new ContextHandler();
        test1Context.setContextPath("/test1");
        test1Context.setHandler(new HelloHandler("Hello1"));

        ContextHandler test2Context = new ContextHandler();
        test2Context.setContextPath("/test2");
        test2Context.setHandler(new HelloHandler("Hello2"));

        ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
        contextHandlers.setHandlers(new Handler[]
        { test1Context, test2Context });

        ServerConnector serverConnector = new ServerConnector(server,
            new SslConnectionFactory(sslContextFactory,"http/1.1"),
            new HttpConnectionFactory(httpConf)); // <-- use it!
        serverConnector.setPort(port);

        server.setConnectors(new Connector[]
        { serverConnector });
        server.setHandler(contextHandlers);

        server.start();
        server.join();
    }
}

这篇关于嵌入式Jetty:在安全的https服务器中,ContextHandler重定向到http URI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆