Https 服务 🌐
This content is not available in your language yet.
出于安全服务需要,生产环境通常使用 Https 协议,Feat 也提供了相应的能力。
下文演示所使用的证书是通过 mkcert 生成的自签名证书。
生成 PEM 证书
执行以下命令生成证书:
mkcert example.com "*.example.com" example.test localhost 127.0.0.1 ::1
如果控制台出现以下提示信息,则表示证书生成成功。
Created a new certificate valid for the following names 📜 - "example.com" - "*.example.com" - "example.test" - "localhost" - "127.0.0.1" - "::1"
Reminder: X.509 wildcards only go one level deep, so this won't match a.b.example.com ℹ️
The certificate is at "./example.com+5.pem" and the key at "./example.com+5-key.pem" ✅
It will expire on 30 April 2027
启动Https服务
将证书文件 example.com+5.pem
和 example.com+5-key.pem
拷贝到项目的 src/main/resources
目录下。
使用 smart-socket 提供的 SslPlugin 插件启动 Https 服务。
public class HttpsPemDemo { public static void main(String[] args) throws Exception { InputStream certPem = HttpsPemDemo.class.getClassLoader().getResourceAsStream("example.org.pem"); InputStream keyPem = HttpsPemDemo.class.getClassLoader().getResourceAsStream("example.org-key.pem"); SslPlugin sslPlugin = new SslPlugin(new PemServerSSLContextFactory(certPem, keyPem)); Feat.httpServer(opt -> opt.addPlugin(sslPlugin)).httpHandler(req -> { req.getResponse().write("Hello Feat Https"); }).listen(); }}
打开浏览器,访问:https://localhost:8080 ,若页面展示如下,说明 Https 服务启动成功。

SSLEngine 传递
HttpRequest 中提供了 getSslEngine()
方法,用于获取 SSLEngine。
但是,SSLEngine 是在底层的网络通信层创建的,应用层无法感知底层是否使用了 SSL 协议。
所以,默认情况下调用 getSslEngine()
方法获取到的 SSLEngine 为 null。
若需要获取 SSLEngine,必须在 SslPlugin 中配置: Consumer<SSLEngine>,
将 SSLEngine
注入到 ThreadLocal 中以供应用层获取。
public class HttpsSSLEngineDemo { public static void main(String[] args) throws Exception { InputStream certPem = HttpsSSLEngineDemo.class.getClassLoader().getResourceAsStream("example.com+5.pem"); InputStream keyPem = HttpsSSLEngineDemo.class.getClassLoader().getResourceAsStream("example.com+5-key.pem"); SslPlugin sslPlugin = new SslPlugin(new PemServerSSLContextFactory(certPem, keyPem), (Consumer<SSLEngine>) sslEngine -> { sslEngine.setUseClientMode(false); 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("engine is null"); } else { req.getResponse().write("engine=" + engine); } }).listen(); }}
TCP 连接建立成功后,应用层Endpoint.java
会在第一时间获取 SSLEngine
。
protected Endpoint(AioSession aioSession, ServerOptions options) { this.aioSession = aioSession; this.options = options; this.sslEngine = HttpRequest.SSL_ENGINE_THREAD_LOCAL.get(); if (sslEngine != null) { HttpRequest.SSL_ENGINE_THREAD_LOCAL.remove(); }}