Loading...

文章背景图

[学习,记录,整理] 在Jetty中开启HTTP2和SSL

2021-03-28
1357
-
- 分钟

代码

在build.gradle中添加相关的依赖

dependencies {
    // 为jetty添加的h2的支持
    implementation group: 'org.eclipse.jetty.http2', name: 'http2-server', version: '9.4.36.v20210114'
    // jetty中http2开启ssl所需的依赖
//    implementation group: 'org.eclipse.jetty', name: 'jetty-alpn-server', version: '9.4.36.v20210114'
    // jdk9之后改用这个
    implementation group: 'org.eclipse.jetty', name: 'jetty-alpn-java-server', version: '9.4.36.v20210114'
}

添加配置类对Jetty进行配置,文件名随意,这里我起的名是JettySslConfiguration。

package run.halo.app.config;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;

@Slf4j
@Configuration
@EnableConfigurationProperties
public class JettySslConfiguration extends AbstractConfiguration implements
    WebServerFactoryCustomizer<JettyServletWebServerFactory> {

    @Value("${server.port}")
    private int https;
    @Value("${server.ssl.port}")
    private int http;
    @Value("${jetty.connect.maxThreads}")
    private int maxThread;
    @Value("${jetty.connect.minThreads}")
    private int minThread;
    @Value("${jetty.connect.idleTimeout}")
    private int idleTimeout;

    @Override
    public void configure(WebAppContext context) throws Exception {
        Constraint constraint = new Constraint();
        constraint.setDataConstraint(Constraint.DC_CONFIDENTIAL);

        ConstraintMapping mapping = new ConstraintMapping();
        mapping.setPathSpec("/*");
        mapping.setConstraint(constraint);

        ConstraintSecurityHandler handler = new ConstraintSecurityHandler();
        handler.addConstraintMapping(mapping);

        context.setSecurityHandler(handler);
    }

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        factory.setConfigurations(Collections.singleton(this));

        factory.addServerCustomizers(
            server -> {
                // 配置线程池
                threadPool(server);

                // 获取服务器的默认连接器
                ServerConnector connector = (ServerConnector) server.getConnectors()[0];

                // 获取SSL上下文工厂
                SslContextFactory sslContextFactory =
                    connector.getConnectionFactory(SslConnectionFactory.class)
                        .getSslContextFactory();
                // 设置SSL密码对比器
                sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);

                // 获取HTTP配置对象
                HttpConfiguration httpConfiguration =
                    connector.getConnectionFactory(HttpConnectionFactory.class)
                        .getHttpConfiguration();

                // 创建SSL连接工厂
                SslConnectionFactory sslConnectionFactory =
                    new SslConnectionFactory(sslContextFactory, "alpn");

                // 创建ALPN连接工厂,ALPN是TLS的扩展,HTTP2需要使用ALPN来开启SSL
                ALPNServerConnectionFactory alpnServerConnectionFactory =
                    new ALPNServerConnectionFactory("h2", "h2-17", "h2-16", "h2-15", "h2-14");

                // 创建HTTP2连接工厂
                HTTP2ServerConnectionFactory http2ServerConnectionFactory =
                    new HTTP2ServerConnectionFactory(httpConfiguration);

                // 创建用于SSL连接的服务器连接器
                ServerConnector httpsConnector =
                    new ServerConnector(server, sslConnectionFactory, alpnServerConnectionFactory,
                        http2ServerConnectionFactory);
                // 设置HTTPS的监听端口
                httpsConnector.setPort(https);

                // 创建用于HTTP连接的服务器连接器
                ServerConnector httpConnector = new ServerConnector(server);
                // 用HTTPS的配置创建一个HTTP的连接工厂,之后通过HTTP的端口访问会重定向到HTTPS的端口
                httpConnector.addConnectionFactory(new HttpConnectionFactory(httpConfiguration));
                // 设置HTTP的监听端口
                httpConnector.setPort(http);

                // 将HTTP和HTTPS的连接器添加(注册)到服务器中
                server.setConnectors(new Connector[] {httpConnector, httpsConnector});
            }
        );
    }

    private void threadPool(Server server) {
        // 线程池
        final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
        // 设置最大线程连接数
        threadPool.setMaxThreads(maxThread);
        // 设置最小线程连接数
        threadPool.setMinThreads(minThread);
        // 设置线程最大空闲时间60000ms
        threadPool.setIdleTimeout(idleTimeout);
    }

}

在编译前或运行前,往application.yaml中添加以下配置。

server:
  # https端口
  port: 8443
  # http端口
  ssl:
    port: 8080
jetty:
  connect:
    maxThreads: 150
    minThreads: 10
    idleTimeout: 30000

上面的配置看着挺让人迷惑,但这是由一个问题造成的。


如果将application.yaml改成这样

server:
  # http端口
  port: 80
  # https端口
  ssl:
    port: 443

java代码改成这样

@Value("${server.port}")
private int http;
@Value("${server.ssl.port}")
private int https;

那么在http://127.0.0.1:80重定向时会出问题,会重定向到https://127.0.0.1:80而不是https://127.0.0.1:443

为了解决这个问题,才让application.yaml中的配置看起来像是人类迷惑行为。当然,也可以不使用Jetty监听HTTP做重定向,可以使用Nginx来监听HTTP再进行重定向,这样就不会有迷惑行为了。


要开启SSL,肯定得加载证书,在部署时,往~/.halo/application.yaml中添加以下配置

server:
  ssl:
    key-store: .halo/证书文件
    key-store-password: 证书密码
    keyStoreType: PKCS12

至于为什么不用Ningx反代来开启SSL...这是个痛点,我的服务器提供商不允许建站机器涉及任何代理行为,反代也在其中。


文章参考

关于Spring Boot中使用Jetty容器开启HTTP2的配置

评论交流

文章目录