嵌入式 Jetty:在安全 https 服务器中,ContextHandler 重定向到 http URI

2024-02-16

我正在使用嵌入式 Jetty 构建沙箱 RESTful API。我的概念验证设计:一个简单的嵌入式 jetty 服务器,(1) 接受 SSL 端口上的连接,(2) 使用 ContextHandlerCollection 根据 URI 前缀调用正确的处理程序。

我最初的测试使用简单的非 SSL 连接,似乎工作得很好(注意,附录中的导入代码和助手 HelloHandler 类)。

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 个多小时的故障排除)。

当我切换到 HTTPS SSL 连接时,一切都出了问题。代码如下:

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/它会起作用的。言语无法充分表达此类误报所造成的挫败感。

除了浏览器重定向和报告连接重置错误之外,我还会在服务器日志中收到以下异常:

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 方案被丢弃,这导致服务器抱怨纯文本。

解决方法:

一旦我清楚了这种重定向行为,对实际问题的快速 Google 搜索就引导我找到了 ContextHandler.setAllowNullPathInfo(true) 方法 - 该方法会关闭此重定向行为。在我上面的代码中,这是通过两行完成的:

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

这篇文章的要点:

我花了 3 - 4 个小时尝试解决“javax.net.ssl.SSLException:无法识别的 SSL 消息,纯文本连接?”异常,并且我在网络上没有找到与上面的解决方案/解决方法链接的异常。如果我能将一名其他开发人员从我所经历的挫败感中拯救出来,那么任务就完成了。

挥之不去的问题(发布此内容的其他原因): 好吧,我让这段代码工作了,但必须承认:我非常不安,因为我的极其简单的概念验证测试,做了一些我认为很常见的事情,遇到了这种似乎完全前所未有的情况。这告诉我,我可能正在做一些超出“最佳实践”范围的事情;或者,更糟糕的是,我的设计方式完全错误。那么,问题:

1)我做错了吗?

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

附录(辅助类和导入的代码) 进口: 导入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 设置。

Here ...

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();
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

嵌入式 Jetty:在安全 https 服务器中,ContextHandler 重定向到 http URI 的相关文章

随机推荐

  • 检查字符串是否包含列表中的元素的更智能方法 - python

    List top brands包含品牌列表 例如 top brands Coca Cola Apple Victoria s Secret items is a pandas DataFrame结构如下图所示 我的任务是填写brand na
  • Visual Studio 2008 类视图缺少类

    我刚刚将一个大型项目从旧版本的 Visual C 移植到 VS2008 并注意到类视图缺少一些我的类 查看解决方案视图 声明这些类的头文件存在 因此我希望在类视图中看到它们 排除某些类的任何原因 或者有什么方法可以刷新类视图以包含解决方案中
  • shiny,DT,styleColorBar 正值和负值的颜色不同?

    我已阅读 DT pdf 文档并进行了搜索 但 DT 包似乎不支持不同的颜色 我怎样才能达到下面图片链接所示的效果 谢谢 示例 正值和负值使用不同的颜色 https asymmetryobservations files wordpress
  • 将 copts/defines 传播到目标的所有依赖项

    我有一个项目 在一个相当复杂的构建系统中的单个工作空间中涉及多个构建文件 简而言之 我的目标 对于某些特定目标 我希望使用一组额外的属性来构建其所有递归依赖项 copts defines 与以任 何其他方式构建这些依赖项目标时相比 我还没有
  • 在 BIN 文件夹中找不到 ApacheJMeterTemporaryRootCA.crt

    我无法在 bin 文件夹中看到 ApacheJMeterTemporaryRootCA crt 文件 我正在运行 jmeter 5 2 1 我需要对此进行排序 以便能够将认证添加到对服务器的 API 调用中 请提供任何帮助或为我指明正确的方
  • 在 C# 中存储永远不会改变的静态数据的最佳方法是什么

    我有一个类将数据存储在 asp net c 应用程序中 并且永远不会改变 我真的不想将这些数据放入数据库中 我希望它保留在应用程序中 这是我在应用程序中存储数据的方法 public class PostVoteTypeFunctions p
  • 从html中提取标题标签

    我想从 html 字符串中提取标题标签的内容 我已经进行了一些搜索 但到目前为止我无法在 VB C 或 PHP 中找到此类代码 此外 这应该适用于大写和小写标签 例如应该与两者一起使用和 TITLE gt 谢谢 您可以为此使用正则表达式 但
  • 如何在 Haskell 中进行嵌套“循环”

    我正在查看一些 JAVA 代码 我想知道如何将其转换为 Haskell IntStream range 0 cookedWords length parallel forEach int i gt int A cookedWords i f
  • 在视图中播放框架配置值

    我如何访问该值application name from conf application conf在一个视图中 您可以使用以下代码示例来执行此操作 play configuration application name 另请参阅http
  • 如何使用 postData 过滤器刷新 jqGrid 搜索对话框

    我正在使用 jqGrid 到目前为止一切都运行良好 但现在我正在努力允许用户能够保存他们的搜索 以便他们以后可以检索它们和 或将它们应用到不同的网格 事实证明 节省实际上是最容易的部分 我可以根据指定的 postData 过滤器获取网格进行
  • Wix 卸载不删除文件,始终假定程序文件\myApp 文件夹

    我在使用 Wix 安装程序时遇到问题 并且不知道具体缺少什么 我已启用该对话框以允许用户选择自己的安装文件夹而不是默认的安装文件夹 如果我运行安装并保留默认的 Program Files MyApp 则应用程序安装没有问题 如果我随后卸载
  • 如何解决 Labview 加载冲突

    我正在 Labview 中开发一个数据采集程序 该程序使用多个平移台 相机 高速数字化仪和其他仪器 我正在一台计算机上开发该应用程序 并将其部署到另一台计算机上 开发计算机具有labview 2013 而要部署应用程序的计算机当前具有Lab
  • 在 Backbone.js 应用程序中设置全局 REST 根 url

    在backbone js中 您必须手动设置每个模型的rooturl 有没有一种方法可以将其设置在一个位置一次并且所有模型都会使用它 For eg api site com将是 REST 服务 但出于测试目的 它可能位于localhost 1
  • Apache NiFi:使用映射值将列添加到 csv

    使用 GetFile 处理器将 csv 导入 NiFi 工作流程 我有一列由 id 组成 每个id代表一个特定的字符串 大约有3个id 例如 如果我的 csv 包含 name age id John 10 Y Jake 55 N Finn
  • 我如何在 php 中获取上个月的名称[重复]

    这个问题在这里已经有答案了 可能的重复 php 中给定月份的下个月和上个月 https stackoverflow com questions 9172035 next and previous month from a given mon
  • EventAggregator 仅适用于 MVVM 中的 ViewModel?

    我了解到在 MVVM 设计中实现的事件聚合器模式可以帮助解耦 ViewModel 之间的通信 我认为事件聚合器确实是个好主意 但转念一想 事件聚合器仅由 ViewModel 使用吗 模型可以在事件聚合器中发布和订阅事件吗 通过这种方式 Vi
  • 创建一个函数以从 Oracle 数据库中获取数据(按 ID 排列的数组)

    我正在尝试创建一个函数 它只允许我向其中传递 SQL 语句 并且它将根据我传递给它的唯一 ID 生成一个数组 function oracleGetGata query id id global conn sql OCI Parse conn
  • appspot.com 网址显示在 Google 搜索结果中,而不是自定义域名

    我已经设置了http www footballverdict com http www footballverdict com它托管在 Google App Engine 上 一切正常 您可以毫无问题地访问自定义域 由于某种原因 当我在 G
  • 在 Swift 中,如何在 Voiceover 说话时停止文本转语音,反之亦然?

    现在 我的应用程序实现了AVSpeechSynthesizer读出每个屏幕的说明 该应用程序还会考虑何时启用 Voiceover 辅助功能 我现在面临的问题是文本转语音功能与画外音功能重叠 是否有解决方案可以检测到当用户导航到屏幕上的另一个
  • 嵌入式 Jetty:在安全 https 服务器中,ContextHandler 重定向到 http URI

    我正在使用嵌入式 Jetty 构建沙箱 RESTful API 我的概念验证设计 一个简单的嵌入式 jetty 服务器 1 接受 SSL 端口上的连接 2 使用 ContextHandlerCollection 根据 URI 前缀调用正确的