## 代码
在build.gradle中添加相关的依赖
```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'
}
```
<br/>
添加配置类对Jetty进行配置,文件名随意,这里我起的名是JettySslConfiguration。
```java
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);
}
}
```
<br/>
在编译前或运行前,往`application.yaml`中添加以下配置。
```yaml
server:
# https端口
port: 8443
# http端口
ssl:
port: 8080
jetty:
connect:
maxThreads: 150
minThreads: 10
idleTimeout: 30000
```
上面的配置看着挺让人迷惑,但这是由一个问题造成的。
<br/>
如果将`application.yaml`改成这样
```yaml
server:
# http端口
port: 80
# https端口
ssl:
port: 443
```
java代码改成这样
```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再进行重定向,这样就不会有迷惑行为了。
<br/>
要开启SSL,肯定得加载证书,在部署时,往`~/.halo/application.yaml`中添加以下配置
```yaml
server:
ssl:
key-store: .halo/证书文件
key-store-password: 证书密码
keyStoreType: PKCS12
```
至于为什么不用Ningx反代来开启SSL...这是个痛点,我的服务器提供商不允许建站机器涉及任何代理行为,反代也在其中。
<br/>
## 文章参考
[关于Spring Boot中使用Jetty容器开启HTTP2的配置](https://www.dazhuanlan.com/2019/10/05/5d97f05365637/)
[学习,记录,整理] 在Jetty中开启HTTP2和SSL