阅读量:0
文章目录
一、HTTPS请求流程
二、具体实现
1.引入依赖
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.15.Final</version> </dependency>
2.生成密钥库文件
keytool -genkey -keysize 2048 -validity 365 -keyalg RSA -dname “CN=localhost” -keypass hsc123 -storepass hsc123 -keystore local.jks
使用Java的keytool
实用程序来生成一个新的密钥库文件(Keystore),其中包含一个自签名的密钥对。下面是这个命令的详细解析:
keytool
: 这是Java自带的一个命令行工具,用于管理密钥库和证书。-genkey
: 指示keytool
生成一个新的密钥对。-keysize 2048
: 设置生成的RSA密钥的长度为2048位。-validity 365
: 设置密钥的有效期为365天。-keyalg RSA
: 指定密钥算法为RSA。-dname “CN=localhost”
: 指定密钥对的主体名(Distinguished Name),在这个例子中,CN=localhost
表示Common Name字段设置为localhost
。-keypass hsc123
: 设置密钥的密码为hsc123
。这个密码用于保护私钥。-storepass hsc123
: 设置密钥库的密码也为hsc123
。这个密码用于保护整个密钥库文件。-keystore local.jks
: 指定要生成的密钥库文件名为local.jks
。
当所有信息输入完毕后,keytool
会在当前目录下生成一个名为local.jks
的文件,该文件包含了一个有效期为一年、2048位的RSA密钥对,用于localhost
的自签名证书。
查看密钥库中的所有别名:
keytool -list -v -keystore local.jks -storepass hsc123
根据别名导出自签名的CA证书:
keytool -export -file localhost.crt -alias mykey -keystore local.jks -storepass hsc123
3.使用Netty搭建服务器
public class HttpsServer { // 启动 HTTPS 服务器的方法,参数为监听端口 public static void start(final int port) throws Exception { // 创建用于处理新连接请求的事件循环组(BOSS) EventLoopGroup boss = new NioEventLoopGroup(); // 创建用于处理已接受连接的数据读写的事件循环组(WORKER) EventLoopGroup worker = new NioEventLoopGroup(); // 创建服务器引导程序 ServerBootstrap serverBootstrap = new ServerBootstrap(); try { // 配置服务器引导程序 serverBootstrap.channel(NioServerSocketChannel.class) // 使用 NIO 实现的服务器套接字通道 .option(ChannelOption.SO_BACKLOG, 1024) // 设置 TCP 参数 SO_BACKLOG 为 1024 .group(boss, worker) // 绑定两个事件循环组 .childHandler(new ChannelInitializer<SocketChannel>() { // 设置子处理器,处理每个连接的初始化 @Override protected void initChannel(SocketChannel ch) throws Exception { // 创建 SSL 上下文并获取 SSLEngine SSLEngine sslEngine = SSLContextFactory.getSslContext().createSSLEngine(); sslEngine.setUseClientMode(false); // 设置为服务器模式 // 添加 SSL 处理器到管道中 ch.pipeline().addLast(new SslHandler(sslEngine)); // 添加 HTTP 解码器到管道中 ch.pipeline().addLast("http-decoder", new HttpServerCodec()); // 添加自定义的 HTTPS 服务器处理器到管道中 ch.pipeline().addLast(new HttpsSeverHandler()); } }); // 绑定服务器到指定端口,并等待完成 ChannelFuture future = serverBootstrap.bind(port).sync(); // 等待服务器 socket 关闭 future.channel().closeFuture().sync(); } finally { // 平滑关闭所有事件循环组 boss.shutdownGracefully(); worker.shutdownGracefully(); } } // 主方法,启动 HTTPS 服务器在端口 7000 public static void main(String[] args) throws Exception { start(7000); } }
初始化 SSL 上下文:SSLContextFactory.getSslContext()
public class SSLContextFactory { // 静态方法用于获取 SSLContext 实例 public static SSLContext getSslContext() throws Exception { // 将密钥库密码转换为字符数组 char[] passArray = "hsc123".toCharArray(); // 获取 SSLContext 实例,使用 TLSv1 协议版本 SSLContext sslContext = SSLContext.getInstance("TLSv1"); // 获取 JKS 类型的 KeyStore 实例 KeyStore ks = KeyStore.getInstance("JKS"); // 加载 keytool 生成的密钥库文件 FileInputStream inputStream = new FileInputStream("/home/qyy/local.jks"); // 从输入流中加载 KeyStore ks.load(inputStream, passArray); // 获取 KeyManagerFactory 实例 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); // 初始化 KeyManagerFactory,使用之前加载的 KeyStore 和密码 kmf.init(ks, passArray); // 初始化 SSLContext,设置 KeyManagers,TrustManagers 和 SecureRandom sslContext.init(kmf.getKeyManagers(), null, null); // 关闭文件输入流 inputStream.close(); // 返回配置好的 SSLContext 实例 return sslContext; } }
处理http请求:HttpsSeverHandler
public class HttpsSeverHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 判断消息是否为 HttpRequest 类型 if (msg instanceof HttpRequest) { HttpRequest request = (HttpRequest) msg; // 转换为 HttpRequest // 检查是否需要保持连接 boolean keepAlive = HttpUtil.isKeepAlive(request); // 打印请求方法和 URI System.out.println("method===" + request.method()); System.out.println("uri===" + request.uri()); // 创建一个默认的 FullHttpResponse FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); // 设置响应体的内容 ByteBuf content = httpResponse.content(); content.writeBytes("返回 https".getBytes()); // 设置 Content-Type 头 httpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8"); // 设置 Content-Length 头 httpResponse.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); // 如果客户端希望保持连接,则设置 Keep-Alive 头 if (keepAlive) { httpResponse.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); // 写入并刷新响应到客户端 ctx.writeAndFlush(httpResponse); } else { // 如果客户端不希望保持连接,则写入并刷新响应到客户端,并在完成后关闭连接 ctx.writeAndFlush(httpResponse).addListener(ChannelFutureListener.CLOSE); } } } }
三、结果
可以查看我们的自签名证书无效