HTTPS 配置
Feat 通过 SslPlugin 插件为 HTTP 服务提供 HTTPS 支持。你不需要重写业务代码,只需添加 SSL 插件即可将 HTTP 服务升级为 HTTPS。
基础 HTTPS 服务
Section titled “基础 HTTPS 服务”使用 PEM 格式证书创建 HTTPS 服务:
import io.github.smartboot.socket.extension.plugins.SslPlugin;import io.github.smartboot.socket.extension.ssl.factory.PemServerSSLContextFactory;import tech.smartboot.feat.Feat;
import java.io.InputStream;
public class HttpsDemo { public static void main(String[] args) throws Exception { // 加载证书文件 InputStream certPem = HttpsDemo.class.getClassLoader() .getResourceAsStream("example.org.pem"); InputStream keyPem = HttpsDemo.class.getClassLoader() .getResourceAsStream("example.org-key.pem");
// 创建 SSL 插件 SslPlugin sslPlugin = new SslPlugin( new PemServerSSLContextFactory(certPem, keyPem) );
// 启动 HTTPS 服务 Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> req.getResponse().write("Hello HTTPS")) .listen(); }}本地开发推荐使用 mkcert 生成本地可信证书:
# 安装 mkcertbrew install mkcert # macOS# 或下载对应系统的二进制文件
# 生成证书mkcert example.com "*.example.com" localhost 127.0.0.1 ::1执行后会生成两个文件:
example.com+4.pem- 证书文件example.com+4-key.pem- 私钥文件
将这两个文件放入项目的 src/main/resources 目录。
SSL 插件配置
Section titled “SSL 插件配置”自动生成的证书
Section titled “自动生成的证书”测试环境可以使用 AutoServerSSLContextFactory 自动生成自签名证书:
import io.github.smartboot.socket.extension.ssl.factory.AutoServerSSLContextFactory;
SslPlugin sslPlugin = new SslPlugin(new AutoServerSSLContextFactory());
Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> req.getResponse().write("Auto HTTPS")) .listen();支持为不同域名配置不同证书:
import io.github.smartboot.socket.extension.ssl.factory.PemServerSSLContextFactory;import java.util.HashMap;import java.util.Map;
// 配置多域名证书Map<String, PemServerSSLContextFactory> certMap = new HashMap<>();certMap.put("example.com", new PemServerSSLContextFactory( certPem1, keyPem1));certMap.put("api.example.com", new PemServerSSLContextFactory( certPem2, keyPem2));
SslPlugin sslPlugin = new SslPlugin( (socketAddress, engine) -> { // 根据域名返回对应证书 return certMap.get(engine.getPeerHost()); });处理 HTTPS 请求
Section titled “处理 HTTPS 请求”HTTPS 请求的处理方式与 HTTP 完全相同:
Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> { // 获取请求信息 String method = req.getMethod(); String uri = req.getRequestURI();
// 设置响应 req.getResponse() .setContentType("application/json") .write("{\"status\":\"ok\"}"); }) .listen(8443);获取 SSL 信息
Section titled “获取 SSL 信息”通过 SSLEngine 获取 TLS 连接详情:
import javax.net.ssl.SSLEngine;import javax.net.ssl.SSLSession;
Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> { SSLEngine engine = req.getSslEngine();
if (engine != null) { SSLSession session = engine.getSession();
// 获取协议版本 String protocol = session.getProtocol(); // TLSv1.3
// 获取加密套件 String cipher = session.getCipherSuite(); // TLS_AES_256_GCM_SHA384
req.getResponse().write("Protocol: " + protocol); } else { req.getResponse().write("Not HTTPS"); } }) .listen();启用 SSLEngine 访问
Section titled “启用 SSLEngine 访问”如需在业务代码中访问 SSLEngine,需要在插件初始化时进行设置:
import io.github.smartboot.socket.extension.ssl.factory.AutoServerSSLContextFactory;import tech.smartboot.feat.core.server.HttpRequest;
import javax.net.ssl.SSLEngine;import java.util.function.Consumer;
SslPlugin sslPlugin = new SslPlugin( new AutoServerSSLContextFactory(), (Consumer<SSLEngine>) sslEngine -> { // 将 SSLEngine 存入 ThreadLocal HttpRequest.SSL_ENGINE_THREAD_LOCAL.set(sslEngine); });
Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> { SSLEngine engine = req.getSslEngine(); if (engine != null) { req.getResponse().write("Cipher: " + engine.getSession().getCipherSuite()); } }) .listen();客户端认证(mTLS)
Section titled “客户端认证(mTLS)”启用双向 TLS 认证,要求客户端提供证书:
import io.github.smartboot.socket.extension.ssl.ClientAuth;
SslPlugin sslPlugin = new SslPlugin( new PemServerSSLContextFactory(certPem, keyPem), ClientAuth.REQUIRE // 要求客户端证书);
Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> { SSLEngine engine = req.getSslEngine(); if (engine != null) { // 获取客户端证书信息 javax.security.cert.X509Certificate[] certs = engine.getSession().getPeerCertificateChain(); req.getResponse().write("Client: " + certs[0].getSubjectDN()); } }) .listen();ClientAuth 模式:
| 模式 | 说明 |
|---|---|
NONE | 不验证客户端证书(默认) |
REQUEST | 请求客户端证书,但不强制 |
REQUIRE | 强制要求客户端证书 |
基础 HTTPS 服务
Section titled “基础 HTTPS 服务”import io.github.smartboot.socket.extension.plugins.SslPlugin;import io.github.smartboot.socket.extension.ssl.factory.PemServerSSLContextFactory;import tech.smartboot.feat.Feat;import tech.smartboot.feat.core.common.HeaderName;import tech.smartboot.feat.core.common.HttpStatus;
import java.io.InputStream;
public class HttpsBasicDemo { public static void main(String[] args) throws Exception { // 1. 加载证书 InputStream certPem = HttpsBasicDemo.class.getClassLoader() .getResourceAsStream("server.pem"); InputStream keyPem = HttpsBasicDemo.class.getClassLoader() .getResourceAsStream("server-key.pem");
if (certPem == null || keyPem == null) { System.err.println("证书文件未找到"); return; }
// 2. 创建 SSL 插件 SslPlugin sslPlugin = new SslPlugin( new PemServerSSLContextFactory(certPem, keyPem) );
// 3. 启动 HTTPS 服务 Feat.httpServer(opt -> { opt.addPlugin(sslPlugin); opt.port(8443); }) .httpHandler(req -> { // 获取协议信息 String protocol = req.getProtocol(); // HTTPS/1.1
req.getResponse() .setHeader(HeaderName.CONTENT_TYPE, "application/json") .write("{\"message\":\"Secure connection established\"}"); }) .listen();
System.out.println("HTTPS server started on https://localhost:8443"); }}带 SSL 信息输出的服务
Section titled “带 SSL 信息输出的服务”import io.github.smartboot.socket.extension.plugins.SslPlugin;import io.github.smartboot.socket.extension.ssl.factory.AutoServerSSLContextFactory;import tech.smartboot.feat.Feat;import tech.smartboot.feat.core.server.HttpRequest;
import javax.net.ssl.SSLEngine;import javax.net.ssl.SSLSession;import java.util.function.Consumer;
public class HttpsInfoDemo { public static void main(String[] args) throws Exception { // 创建带 SSLEngine 回调的 SSL 插件 SslPlugin sslPlugin = new SslPlugin( new AutoServerSSLContextFactory(), (Consumer<SSLEngine>) sslEngine -> { HttpRequest.SSL_ENGINE_THREAD_LOCAL.set(sslEngine); } );
Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> { SSLEngine engine = req.getSslEngine(); StringBuilder info = new StringBuilder();
if (engine != null) { SSLSession session = engine.getSession(); info.append("Protocol: ").append(session.getProtocol()).append("\n"); info.append("Cipher Suite: ").append(session.getCipherSuite()).append("\n"); info.append("Peer Host: ").append(session.getPeerHost()).append("\n"); }
req.getResponse().write(info.toString()); }) .listen(); }}浏览器提示证书不受信任
Section titled “浏览器提示证书不受信任”原因:使用的是自签名证书。
解决:
- 本地开发使用
mkcert生成可信证书 - 生产环境使用正规 CA 签发的证书
- 测试环境可手动将证书添加到浏览器信任列表
证书文件加载失败
Section titled “证书文件加载失败”排查清单:
- 确认文件名与代码中一致
- 确认文件在
src/main/resources目录下 - 确认文件已打包进 jar(检查构建输出)
- 使用绝对路径测试:
InputStream cert = new FileInputStream("/absolute/path/to/cert.pem");
HTTPS 无法访问
Section titled “HTTPS 无法访问”检查项:
- 确认访问的是
https://而非http:// - 确认端口正确(默认 8080,除非显式指定)
- 确认防火墙允许该端口
- 检查控制台是否有 SSL 相关错误日志
SSLEngine 返回 null
Section titled “SSLEngine 返回 null”原因:默认情况下 SSLEngine 不会自动设置到请求对象。
解决:创建 SslPlugin 时添加回调:
SslPlugin sslPlugin = new SslPlugin( sslContextFactory, (Consumer<SSLEngine>) sslEngine -> { HttpRequest.SSL_ENGINE_THREAD_LOCAL.set(sslEngine); });HTTP/2 支持
Section titled “HTTP/2 支持”Feat 的 HTTPS 支持 HTTP/2 协议,无需额外配置。客户端使用 ALPN 协商协议版本:
// 服务端代码无需修改,自动支持 HTTP/2Feat.httpServer(opt -> opt.addPlugin(sslPlugin)) .httpHandler(req -> { // req.getProtocol() 返回 HTTP/2.0 或 HTTP/1.1 req.getResponse().write("Protocol: " + req.getProtocol()); }) .listen();