依赖

在netty中,如果想使用ssl的功能,除了netty默认需要的依赖以外,还需要一些其他的依赖以支持ssl

1
2
3
4
5
6
7
8
9
10
11
12
13
<!--netty对SSL/TLS的支持,可以在 Netty 中使用 BoringSSL 提供的 SSL/TLS 功能,而无需依赖于系统中已安装的 OpenSSL 库-->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.51.Final</version>
</dependency>

<!--这是jdk提供的加密套件,Bouncy Castle 是一个流行的开源加密库,提供了丰富的加密算法和安全功能-->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.70</version>
</dependency>

代码

服务端的代码与客户端的代码基本类似,也是通过userEventTriggered获取ALPN协商的协议后,决定代码所使用的协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package com.example.netty.server;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http2.*;
import io.netty.handler.ssl.*;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.CharsetUtil;
import io.netty.util.internal.StringUtil;

import javax.net.ssl.SSLException;
import java.security.cert.CertificateException;
import java.util.List;

public class Https2Server {

public static void main(String[] args) throws CertificateException, InterruptedException, SSLException {
// Generate a self-signed SSL certificate
SelfSignedCertificate cert = new SelfSignedCertificate();
SslContext sslContext = SslContextBuilder
.forServer(cert.certificate(), cert.privateKey())
.sslProvider(SslProvider.OPENSSL)
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)//配置 HTTP/2 协议中支持的密码套件列表,并使用 SupportedCipherSuiteFilter 进行过滤。
.trustManager(InsecureTrustManagerFactory.INSTANCE)//在服务端使用非认证的证书时,必须使用该代码
.applicationProtocolConfig(new ApplicationProtocolConfig(
ApplicationProtocolConfig.Protocol.ALPN,
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2,
ApplicationProtocolNames.HTTP_1_1))
.build();

// Create event loop groups
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

try {
// Create and configure the server bootstrap
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.localAddress(8080)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast(sslContext.newHandler(ch.alloc()))
.addLast(new FrontHandler());
}
});

// Bind and start the server
ChannelFuture future = bootstrap.bind().sync();
System.out.println("Server started and listening on port 8080");

// Wait until the server socket is closed
future.channel().closeFuture().sync();
} finally {
// Shut down the event loop groups
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

static class FrontHandler extends ChannelInboundHandlerAdapter {

private final HttpServerCodec httpServerCodec;
private final HttpObjectAggregator httpObjectAggregator;
private final HttpServerHandler httpServerHandler;
private final Http2FrameCodec http2FrameCodec;
private final Http2ServerHandler http2ServerHandler;

public FrontHandler() {
httpServerCodec = new HttpServerCodec();
httpObjectAggregator = new HttpObjectAggregator(10240);
httpServerHandler = new HttpServerHandler();
http2FrameCodec = Http2FrameCodecBuilder.forServer().build();
http2ServerHandler = new Http2ServerHandler();
}

@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
ctx.pipeline().addLast(httpServerCodec).addLast(httpObjectAggregator).addLast(httpServerHandler);
}

//sslHandler处理完协议协商后,会触发SslHandshakeCompletionEvent,可以通过SslHandshakeCompletionEvent修改客户端使用协议
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof SslHandshakeCompletionEvent){
SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
String protocol = sslHandler.applicationProtocol();
if (StrUtil.isBlank(protocol)){
return;
}
if (protocol.equals(ApplicationProtocolNames.HTTP_2)){
ctx.pipeline().remove(httpServerCodec).remove(httpObjectAggregator).remove(httpServerHandler).addLast(http2FrameCodec).addLast(http2ServerHandler);
ctx.pipeline().remove(this);
}else if (protocol.equals(ApplicationProtocolNames.HTTP_1_1)){
ctx.pipeline().remove(this);
}
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}

}

@ChannelHandler.Sharable
static class Http2ServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof DefaultHttp2HeadersFrame) {
DefaultHttp2HeadersFrame http2HeadersFrame = (DefaultHttp2HeadersFrame) msg;
if (http2HeadersFrame.isEndStream()){
String responseBody = "http/2";
Http2FrameStream requestStream = http2HeadersFrame.stream();
response(ctx, requestStream, responseBody);
}
} else if (msg instanceof DefaultHttp2DataFrame) {
DefaultHttp2DataFrame http2RequestDataFrame = (DefaultHttp2DataFrame) msg;

String requestBody = http2RequestDataFrame.content().toString(CharsetUtil.UTF_8);
System.out.println(http2RequestDataFrame.stream().id() + ":" + requestBody);

String responseBody = "http/2:" + requestBody;
Http2FrameStream requestStream = http2RequestDataFrame.stream();
response(ctx, requestStream, responseBody);
} else if (msg instanceof DefaultHttp2PingFrame) {
if (((DefaultHttp2PingFrame) msg).ack()) {
DefaultHttp2PingFrame pingFrame = new DefaultHttp2PingFrame(1);
ctx.writeAndFlush(pingFrame);
}
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}

private void response(ChannelHandlerContext ctx, Http2FrameStream requestStream, String responseBody){
DefaultHttp2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(
new DefaultHttp2Headers().status("200")
.set(HttpHeaderNames.DATE, DateTime.now().toString())
.set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8")
.set(HttpHeaderNames.CONTENT_LENGTH, String.valueOf(responseBody.length()))
).stream(requestStream);
DefaultHttp2DataFrame dataFrame = new DefaultHttp2DataFrame(Unpooled.wrappedBuffer(responseBody.getBytes()), true).stream(requestStream);
ctx.writeAndFlush(headersFrame);
ctx.writeAndFlush(dataFrame);
}
}

@ChannelHandler.Sharable
static class HttpServerHandler extends ChannelInboundHandlerAdapter{

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpMessage){
DefaultFullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer("http/1.1".getBytes()));
httpResponse.headers().set(HttpHeaderNames.CONTENT_LENGTH, "http/1.1".length());
httpResponse.headers().set(HttpHeaderNames.CONNECTION, "keep-alive");
ctx.writeAndFlush(httpResponse);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
}
}
}


本站总访问量:次本站总访问数:人
本站由 Peng Tong 使用 Stellar 1.29.1 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。