前言:
而今你们对“https搭建教程netty”可能比较珍视,咱们都需要了解一些“https搭建教程netty”的相关知识。那么小编在网摘上搜集了一些有关“https搭建教程netty””的相关知识,希望看官们能喜欢,兄弟们一起来学习一下吧!前言
HTTP编码器、解码器,Netty为HTTP消息提供了ChannelHandler编码器和解码器:
1、HttpRequestEncoder:编码器,用于客户端,向服务器发送请求。
2、HttpResponseEecoder:编码器,用于服务端,向客户端发送响应。
3、HttpResponseDecoder:解码器,用于客户端,接收来自服务端的请求。
4、HttpRequestDecoder:解码器,用于服务端,接收来自客户端的请求。
除独立的编码器、解码器,Netty还提供了其它的编解码器:
1、HttpClientCodec:用于客户端的编解码器,等效于HttpRequestEncoder和HttpResponseDecoder的组合。
2、HttpServerCodec:用于服务端的编解码器,等效于HttpRequsetDecoder和HttpResponseEncoder的组合。
3、HttpServerCodec:实现了ChannelInboundHandler和ChannelOutboundHandler,所以具备服务端编码和解码能力。
HttpObjectAggregator聚合器:
上述解码器会将每个HTTP消息中生成多个消息对象,如:HttpRequest、HttpResponse,HttpContent、LastHttpContent,使用聚合器可以将多个消息体合并为一个FullHttpRequest或者FullHttpResponse消息,便于处理请求和响应,Netty提供了HttpObjectAggregator聚合器完成该工作。
SslHandler
SslHandler的ChannelHandler实现HTTPS报文加密和解密的功能,其中SslHandler在内部使用SSLEngine来完成实际的工作,SSLEngine的实现可以是JDK的SSLEngine,也可以是Netty 的OpenSslEngine,推荐使用Netty的OpenSslEngine,它性能更好,通过SslHandler进行解密和加密的过程。
一般情况下,SslHandler在ChannelPipeline中是第一个ChannelHandler,确保只有在所有其它的ChannelHandler将它们的逻辑应用到数据之后,才会进行加密。
参考:SSL认证,代码案例,单向认证和双向认证的代码分享
实现HTTP服务
package com.what21.netty02.demo01.http;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import java.net.InetSocketAddress;public class HttpServer { private String serverName; private String bindHost; private int bindPort; public HttpServer(String serverName, String bindHost, int bindPort) { this.serverName = serverName; this.bindHost = bindHost; this.bindPort = bindPort; } public void start() throws Exception { // 用于Acceptor的主"线程池" EventLoopGroup bossEventGroup = new NioEventLoopGroup(); // 初始化==>用于I/O工作的从"线程池" EventLoopGroup workerEventGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); // group方法设置主从线程池 serverBootstrap.group(bossEventGroup, workerEventGroup); // 指定通道channel类型,服务端为:NioServerSocketChannel serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); serverBootstrap.childHandler(new HttpServerInitializer()); ChannelFuture bindFuture = null; if ("0.0.0.0".equalsIgnoreCase(this.bindHost)) { bindFuture = serverBootstrap.bind(this.bindPort).sync(); } else { InetSocketAddress bindAddress = new InetSocketAddress(this.bindHost, this.bindPort); bindFuture = serverBootstrap.bind(bindAddress).sync(); } Channel parentChannel = bindFuture.channel(); bindFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { System.out.println(HttpServer.this.serverName + ",绑定监听成功," + channelFuture.channel().localAddress()); } else { System.err.println(HttpServer.this.serverName + ",绑定监听失败!" + channelFuture.cause()); } } }); ChannelFuture closeFuture = bindFuture.channel().closeFuture().sync(); closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { System.out.println(HttpServer.this.serverName + ",停止监听成功," + channelFuture.channel().localAddress()); } else { System.err.println(HttpServer.this.serverName + ",停止监听失败!" + channelFuture.cause()); } } }); } catch (Exception e) { e.printStackTrace(); } finally { // 优雅退出,释放"线程池" if (bossEventGroup != null) { bossEventGroup.shutdownGracefully(); } if (workerEventGroup != null) { workerEventGroup.shutdownGracefully(); } } } public static void main(String[] args) { try { new HttpServer("Web服务器","0.0.0.0",8888).start(); } catch (Exception e) { e.printStackTrace(); } }}
package com.what21.netty02.demo01.http;import io.netty.channel.ChannelHandler;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpServerCodec;import java.util.Map;public class HttpServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); // http 编解码 pipeline.addLast(new HttpServerCodec()); // http 消息聚合器 512*1024为接收的最大contentlength pipeline.addLast("httpAggregator", new HttpObjectAggregator(512 * 1024)); // 请求处理器 pipeline.addLast(new HttpRequestHandler()); // 打印一下,每次http请求都打印 Map<String, ChannelHandler> handlerMap = pipeline.toMap(); for (String key : handlerMap.keySet()) { System.out.println(key + "=" + handlerMap.get(key)); } }}
package com.what21.netty02.demo01.http;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.handler.codec.http.*;import io.netty.util.CharsetUtil;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.net.SocketAddress;import java.util.HashMap;import java.util.Map;public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception { //100 Continue if (HttpUtil.is100ContinueExpected(httpRequest)) { ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); } // 远程地址 SocketAddress clientSocketAddress = ctx.channel().remoteAddress(); String uri = httpRequest.uri(); if (uri.equals("/")) { uri = "/index.html"; } String physicalPath = "D:/Users/www/dist/"; File file = new File(physicalPath + uri); if (!file.exists()) { file = new File(physicalPath + "/index.html"); } try { byte[] responseBytes = getContent(file.getPath()); ByteBuf content = Unpooled.copiedBuffer(responseBytes); boolean validateHeaders = false; FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content, validateHeaders); ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE); } catch (IOException e) { e.printStackTrace(); HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR; FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus); ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE); } } /** * @param filePath * @return * @throws IOException */ private byte[] getContent(String filePath) throws IOException { File file = new File(filePath); long fileSize = file.length(); if (fileSize > Integer.MAX_VALUE) { return null; } FileInputStream fi = new FileInputStream(file); byte[] buffer = new byte[(int) fileSize]; int offset = 0; int numRead = 0; while (offset < buffer.length && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) { offset += numRead; } // 确保所有数据均被读取 if (offset != buffer.length) { throw new IOException("Could not completely read file " + file.getName()); } fi.close(); return buffer; } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); }}实现HTTP/HTTPS服务,共用端口
package com.what21.netty02.demo01.https;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.logging.LogLevel;import io.netty.handler.logging.LoggingHandler;import java.net.InetSocketAddress;public class HttpServer { private String serverName; private String bindHost; private int bindPort; public HttpServer(String serverName, String bindHost, int bindPort) { this.serverName = serverName; this.bindHost = bindHost; this.bindPort = bindPort; } public void start() throws Exception { // 用于Acceptor的主"线程池" EventLoopGroup bossEventGroup = new NioEventLoopGroup(); // 初始化==>用于I/O工作的从"线程池" EventLoopGroup workerEventGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); // group方法设置主从线程池 serverBootstrap.group(bossEventGroup, workerEventGroup); // 指定通道channel类型,服务端为:NioServerSocketChannel serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024); serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true); serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); serverBootstrap.childHandler(new HttpServerInitializer()); ChannelFuture bindFuture = null; if ("0.0.0.0".equalsIgnoreCase(this.bindHost)) { bindFuture = serverBootstrap.bind(this.bindPort).sync(); } else { InetSocketAddress bindAddress = new InetSocketAddress(this.bindHost, this.bindPort); bindFuture = serverBootstrap.bind(bindAddress).sync(); } Channel parentChannel = bindFuture.channel(); bindFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { System.out.println(HttpServer.this.serverName + ",绑定监听成功," + channelFuture.channel().localAddress()); } else { System.err.println(HttpServer.this.serverName + ",绑定监听失败!" + channelFuture.cause()); } } }); ChannelFuture closeFuture = bindFuture.channel().closeFuture().sync(); closeFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture channelFuture) throws Exception { if (channelFuture.isSuccess()) { System.out.println(HttpServer.this.serverName + ",停止监听成功," + channelFuture.channel().localAddress()); } else { System.err.println(HttpServer.this.serverName + ",停止监听失败!" + channelFuture.cause()); } } }); } catch (Exception e) { e.printStackTrace(); } finally { // 优雅退出,释放"线程池" if (bossEventGroup != null) { bossEventGroup.shutdownGracefully(); } if (workerEventGroup != null) { workerEventGroup.shutdownGracefully(); } } } public static void main(String[] args) { try { new HttpServer("Web服务器(https)","0.0.0.0",8443).start(); } catch (Exception e) { e.printStackTrace(); } }}
package com.what21.netty02.demo01.https;import com.what21.netty01.demo01.cert2.KeyStoreUtils;import com.what21.netty02.demo01.http.HttpRequestHandler;import io.netty.buffer.ByteBuf;import io.netty.channel.*;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpServerCodec;import io.netty.handler.ssl.SslContext;import io.netty.handler.ssl.SslContextBuilder;import io.netty.handler.ssl.SslHandler;import java.security.PrivateKey;import java.security.cert.Certificate;import java.security.cert.X509Certificate;import java.util.Map;public class HttpServerInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); //RuleBasedIpFilter ruleBasedIpFilter = new RuleBasedIpFilter(rule1, rejectAll); //channel.pipeline().addLast("ipFilter", ruleBasedIpFilter); // http 编解码 channel.pipeline().addLast("httpServerCodec", new HttpServerCodec()); // http 消息聚合器 512*1024为接收的最大contentlength channel.pipeline().addLast("httpObjectAggregator", new HttpObjectAggregator(512 * 1024)); // 请求处理器 channel.pipeline().addLast("serverHandler", new HttpRequestHandler()); // HTTPS支持 channel.pipeline().addFirst(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (((ByteBuf) msg).getByte(0) == 22) { KeyStoreUtils.KeyStoreEntry keyStoreEntry = KeyStoreUtils.readToKeyStoreEntry(); PrivateKey privateKey = keyStoreEntry.getPrivateKey(); Certificate certificate = keyStoreEntry.getCertificate(); SslContext sslContext = SslContextBuilder.forServer(privateKey, (X509Certificate) certificate).build(); SslHandler sslHandler = sslContext.newHandler(channel.alloc()); // 将处理 https 的处理程序添加至 HttpServerCodec 之前 ctx.pipeline().addBefore("httpServerCodec", "sslHandler", sslHandler); } ctx.pipeline().remove(this); super.channelRead(ctx, msg); } }); // 打印一下,每次http请求都打印 Map<String, ChannelHandler> handlerMap = pipeline.toMap(); for (String key : handlerMap.keySet()) { System.out.println(key + "=" + handlerMap.get(key)); } }}
package com.what21.netty02.demo01.https;import lombok.Data;import sun.misc.BASE64Encoder;import java.io.FileInputStream;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.cert.Certificate;public class KeyStoreUtils { /** * @param storePath 秘钥库文件路径 * @param storePasswd 秘钥库密码 * @param alias 证书别名 * @param trustPasswd 证书密码 * @return * @throws Exception */ public static KeyStoreEntry readToKeyStoreEntry(String storePath, String storePasswd, String alias, String trustPasswd) throws Exception { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream(storePath), storePasswd.toCharArray()); PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, trustPasswd.toCharArray()); PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey(); Certificate certificate = keyStore.getCertificate(alias); BASE64Encoder encoder = new BASE64Encoder(); String privateKeyEncoded = encoder.encode(privateKey.getEncoded()); String publicKeyEncoded = encoder.encode(publicKey.getEncoded()); KeyStoreEntry keyStoreEntry = new KeyStoreEntry(); keyStoreEntry.setKeyStore(keyStore); keyStoreEntry.setPrivateKey(privateKey); keyStoreEntry.setPublicKey(publicKey); keyStoreEntry.setCertificate(certificate); keyStoreEntry.setPrivateKeyEncoded(privateKeyEncoded); keyStoreEntry.setPublicKeyEncoded(publicKeyEncoded); return keyStoreEntry; } @Data public static class KeyStoreEntry { private KeyStore keyStore; private PrivateKey privateKey; private PublicKey publicKey; private Certificate certificate; private String privateKeyEncoded; private String publicKeyEncoded; } @Data public static class KeyStoreParam { public String storePath = "d:/server.jks"; public String storePasswd = "123456"; public String alias = "server"; public String trustPasswd = "123456"; } /** * @param keyStoreParam * @return */ public static KeyStoreEntry readToKeyStoreEntry(KeyStoreParam keyStoreParam) { try { return readToKeyStoreEntry(keyStoreParam.getStorePath(), keyStoreParam.getStorePasswd(), keyStoreParam.getAlias(), keyStoreParam.getTrustPasswd()); } catch (Exception e) { e.printStackTrace(); } return null; } /** * @return */ public static KeyStoreEntry readToKeyStoreEntry() { String storePath = "d:/server.jks"; String storePasswd = "123456"; String alias = "server"; String trustPasswd = "123456"; try { return readToKeyStoreEntry(storePath, storePasswd, alias, trustPasswd); } catch (Exception e) { e.printStackTrace(); } return null; }}
package com.what21.netty02.demo01.https;import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelFutureListener;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.SimpleChannelInboundHandler;import io.netty.handler.codec.http.*;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.net.SocketAddress;public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { @Override protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception { //100 Continue if (HttpUtil.is100ContinueExpected(httpRequest)) { ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE)); } // 远程地址 SocketAddress clientSocketAddress = ctx.channel().remoteAddress(); String uri = httpRequest.uri(); if (uri.equals("/")) { uri = "/index.html"; } String physicalPath = "D:/Users/www/dist/"; File file = new File(physicalPath + uri); if (!file.exists()) { file = new File(physicalPath + "/index.html"); } try { byte[] responseBytes = getContent(file.getPath()); ByteBuf content = Unpooled.copiedBuffer(responseBytes); boolean validateHeaders = false; FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content, validateHeaders); ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE); } catch (IOException e) { e.printStackTrace(); HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR; FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus); ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE); } } /** * @param filePath * @return * @throws IOException */ private byte[] getContent(String filePath) throws IOException { File file = new File(filePath); long fileSize = file.length(); if (fileSize > Integer.MAX_VALUE) { return null; } FileInputStream fi = new FileInputStream(file); byte[] buffer = new byte[(int) fileSize]; int offset = 0; int numRead = 0; while (offset < buffer.length && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) { offset += numRead; } // 确保所有数据均被读取 if (offset != buffer.length) { throw new IOException("Could not completely read file " + file.getName()); } fi.close(); return buffer; } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); }}