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

Spring RestTemplate不允许持久https连接

白星渊
2023-03-14

在通过HTTPS访问REST api时,我希望使用Spring RestTemplate使用持久http连接。我不能使它工作;为每个请求创建一个新的连接,并且每次都进行SSL握手。有没有可能通过https使用RestTemplate进行可重用连接,如果有的话,如何配置它?

我设置了一个RestTemplate来通过HTTPS发出请求。那是正确的。然而,我在日志中注意到,每个请求都会发生一次新的SSL握手。

@Before
public void setupPersistentHttpConnectionBackedRestTemplate() {
    final SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
        sslContext,
        new String[] { "TLSv1.2" },
        null,
        SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
        .register("https", sslSocketFactory)
        .build();
    final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
    final CloseableHttpClient httpClient = HttpClients.custom()
        .setSSLSocketFactory(sslSocketFactory)
        .setConnectionManager(connectionManager)
        .build();
    final HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    restTemplate.getRestTemplate().setRequestFactory(requestFactory);
}

然后我使用这个RestTemplate进行了几次调用,如下所示:

ResponseEntity<String> response = restTemplate.exchange("/tomcat/sleep?millis={millis}", HttpMethod.GET, HttpEntity.EMPTY, String.class, SLEEP_DURATION);

我研究了spring-mvc和apache的代码,并注意到以下内容。在Spring RestTemplate execute方法中,创建一个新的请求,然后执行该请求并返回结果。

java prettyprint-override">            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            return (responseExtractor != null ? responseExtractor.extractData(response) : null);

这又以调用HttpComponentsClientHttpRequestFactory结束,在这里每次都会创建一个新的http上下文:


    @Override
    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
        HttpClient client = getHttpClient();

        HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri);
        postProcessHttpRequest(httpRequest);
        HttpContext context = createHttpContext(httpMethod, uri);
        if (context == null) {
            context = HttpClientContext.create();
        }
...
    @Override
    public CloseableHttpResponse execute(
            final HttpRoute route,
            final HttpRequestWrapper request,
            final HttpClientContext context,
            final HttpExecutionAware execAware) throws IOException, HttpException {
...
        Object userToken = context.getUserToken();

        final ConnectionRequest connRequest = connManager.requestConnection(route, userToken);
...
            if (userToken == null) {
                userToken = userTokenHandler.getUserToken(context);
                context.setAttribute(HttpClientContext.USER_TOKEN, userToken);
            }
            if (userToken != null) {
                connHolder.setState(userToken);
            }
...
    @Override
    public Object getUserToken(final HttpContext context) {
...
        if (userPrincipal == null) {
            final HttpConnection conn = clientContext.getConnection();
            if (conn.isOpen() && conn instanceof ManagedHttpClientConnection) {
                final SSLSession sslsession = ((ManagedHttpClientConnection) conn).getSSLSession();
                if (sslsession != null) {
                    userPrincipal = sslsession.getLocalPrincipal();
                }
            }
        }
    public Principal getLocalPrincipal() {
        if (this.cipherSuite.keyExchange != KeyExchange.K_KRB5 && this.cipherSuite.keyExchange != KeyExchange.K_KRB5_EXPORT) {
            return this.localCerts == null ? null : this.localCerts[0].getSubjectX500Principal();
        } else {
            return this.localPrincipal == null ? null : this.localPrincipal;
        }
    }

但是,由于RestTemplate每次都从带有新上下文的新请求开始,uset令牌会丢失,PoolingHttpClientConnectionManager无法找到可重用的连接,因此每次都创建一个新的连接。

我希望RestRemplate可以创建一个重新使用该连接的请求,而不是每次都创建一个新的连接。

共有1个答案

何长恨
2023-03-14

我试图实现同样的目标,我看到的唯一方法是通过扩展HttpComponentSclientTtpRequestFactory来设置一个UserToken,在本例中是一个主体Cert.GetSubjectDN()然后覆盖createHttpContext(HttpMethod HttpMethod,URI URI)

@Override
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
    HttpClientContext context = HttpClientContext.create();
    context.setUserToken(userToken);
    return context;
}
 类似资料:
  • 问题内容: 有时需要允许不安全的HTTPS连接,例如在某些可与任何站点一起使用的网络爬网应用程序中。我将这样的解决方案与旧的HttpsURLConnection API一起使用,该解决方案最近已由JDK 11中的新HttpClient API 取代。通过该新API允许不安全的HTTPS连接(自签名或过期证书)的方法是什么? UPD:我尝试过的代码(在Kotlin中,但直接映射到Java): 但是在

  • 问题内容: 尝试将对象保存到数据库时遇到了以下错误。我尝试了here1和here2提到的解决方案,但是效果不好。我遵循的是一个教程,但唯一的区别是Spring和Hibernate的版本。 我可以直接使用SessionFactory持久化对象,但是如果我使用HibernateDaoSupport尝试使用此方法,它将失败并显示以下错误 spring.xml CustomerDAOImpl.java 使

  • 问题内容: 简而言之,是否有一些适合高使用率和服务器的产品,还是我需要坚持不懈地改进?如果是这样,为什么他们将其删除? 问题答案: PHP 5.3中引入了对此的支持。对于此之前的版本,PDO和–上帝禁止– mysql扩展是唯一的选择。 引用手册: 与mysql扩展不同,mysqli不提供单独的功能来打开持久连接。要打开持久连接,连接时必须在主机名前加p:。

  • “持久连接”特性允许利用一个单独的网络连接来运行本来需要多个连接的多个命令。 6.8.1.Open (打开连接) 原型:function Open () 打开到服务器的持久连接。 6.8.2.Close (关闭连接) 原型:function Close () 关闭先前打开的持久连接。

  • 问题内容: 我一直在研究从动态Web应用程序中提取信息的程序,该程序运行良好,直到我将tomcat服务器设置为使用具有自签名(因此,不受信任)证书的SSL。错误的堆栈跟踪为: 在Web浏览器中,当用户使用不受信任的证书访问HTTPS站点时,系统会提示用户警告,并要求他设置是否愿意继续进行操作;我想为我的命令行应用程序实现类似的功能…我承认我是套接字编程和网络领域的新手。解决这个问题的任何建议都会很

  • 问题内容: 我尝试使用连接器8.0.11将MySql数据库与Java连接。一切似乎都还可以,但是我有一个例外: 线程“主”中的异常java.sql.SQLNonTransientConnectionException: 不允许公共密钥检索 堆栈跟踪: 连接器管理器: 问题答案: 您应该将客户端选项添加到mysql- connector中,以允许客户端自动从服务器请求公共密钥。请注意,这可能允许恶意