使用 SSL/TLS 加密 Netty 程序
今天数据隐私是一个十分关注的问题,作为开发人员,我们需要准备好解决这个问题。至少我们需要熟悉加密协议 SSL 和 TLS 等之上的其他协议实现数据安全。作为一个 HTTPS 网站的用户,你是安全。当然,这些协议是广泛不基于 http 的应用程序,例如安全SMTP(SMTPS)邮件服务,甚至关系数据库系统。
为了支持 SSL/TLS,Java 提供了 javax.net.ssl API 的类SslContext 和 SslEngine 使它相对简单的实现解密和加密。Netty 的利用该 API 命名 SslHandler 的 ChannelHandler 实现, 有一个内部 SslEngine 做实际的工作。
图1显示了一个使用 SslHandler 数据流图。
- 加密的入站数据被 SslHandler 拦截,并被解密
- 前面加密的数据被 SslHandler 解密
- 平常数据传过 SslHandler
- SslHandler 加密数据并它传递出站
代码2.0所示一个 SslHandler 使用 ChannelInitializer 添加到 ChannelPipeline。(回想一下,
当 Channel 注册时 ChannelInitializer 用于设置 ChannelPipeline 。)
代码2.0 Add SSL/TLS support
|
|
- 使用构造函数来传递 SSLContext 用于使用(startTls 是否启用)
- 从 SslContext 获得一个新的 SslEngine 。给每个 SslHandler 实例使用一个新的SslEngine
- 设置 SslEngine 是 client 或者是 server 模式
- 添加 SslHandler 到 pipeline 作为第一个处理器
在大多数情况下,SslHandler 将成为 ChannelPipeline 中的第一个 ChannelHandler 。这将确保所有其他 ChannelHandler 应用他们的逻辑到数据后加密后才发生,从而确保他们的变化是安全的。
SslHandler 有很多有用的方法。例如,在握手阶段两端相互验证,商定一个加密方法。您可以配置 SslHandler 修改其行为或提供 在SSL/TLS 握手完成后发送通知,这样所有数据都将被加密。 SSL/TLS 握手将自动执行。
构建 Netty HTTP/HTTPS 应用
HTTP Decoder, Encoder 和 Codec
HTTP 是请求-响应模式,客户端发送一个 HTTP 请求,服务就响应此请求。Netty 提供了简单的编码、解码器来简化基于这个协议的开发工作。图8.2和图8.3显示 HTTP 请求和响应的方法是如何生产和消费的
- HTTP Request 第一部分是包含的头信息
- HttpContent 里面包含的是数据,可以后续有多个 HttpContent 部分
- LastHttpContent 标记是 HTTP request 的结束,同时可能包含头的尾部信息
- 完整的 HTTP request
HTTP 请求/响应可能包含不止一个数据部分,它总是终止于LastHttpContent 部分。FullHttpRequest 和FullHttpResponse 消息是特殊子类型,分别表示一个完整的请求和响应。所有类型的 HTTP 消息(FullHttpRequest ,LastHttpContent 等)实现 HttpObject 接口。
代码2.1所示的是将支持 HTTP 添加到您的应用程序是多么简单。仅仅添加正确的ChannelHandler 到 ChannelPipeline 中
代码2.1 Add support for HTTP
|
|
- client: 添加 HttpResponseDecoder 用于处理来自 server 响应
- client: 添加 HttpRequestEncoder 用于发送请求到 server
- server: 添加 HttpRequestDecoder 用于接收来自 client 的请求
- server: 添加 HttpResponseEncoder 用来发送响应给 client
HTTP消息聚合
安装 ChannelPipeline 中的初始化之后,你能够对不同 HttpObject 消息进行操作。但由于HTTP 请求和响应可以由许多部分组合而成,你需要聚合他们形成完整的消息。为了消除这种繁琐任务, Netty 提供了一个聚合器,合并消息部件到 FullHttpRequest 和 FullHttpResponse消息。这样您总是能够看到完整的消息内容。
这个操作有一个轻微的成本,消息段需要缓冲,直到完全可以将消息转发到下一个ChannelInboundHandler 管道。但好处是,你不必担心消息碎片。
实现自动聚合只需添加另一个 ChannelHandler 到 ChannelPipeline。代码2.2显示了这是如何实现的。
代码2.2 自动聚合HTTP消息片段
|
|
- client: 添加 HttpClientCodec
- server: 添加 HttpServerCodec 作为我们是 server 模式时
- 添加 HttpObjectAggregator 到 ChannelPipeline, 使用最大消息值是 512kb
HTTP 压缩
使用 HTTP 时建议压缩数据以减少传输流量,压缩数据会增加 CPU 负载,现在的硬件设施都很强大,大多数时候压缩数据时一个好主意。Netty 支持“gzip”和“deflate”,为此提供了两个ChannelHandler 实现分别用于压缩和解压。看下面代码:
HTTP Request Header
客户端可以通过提供下面的头显示支持加密模式。然而服务器不是,所以不得不压缩它发送的数据。
|
|
例子:
代码2.3 自动压缩HTTP消息
|
|
- client: 添加 HttpClientCodec
- client: 添加 HttpContentDecompressor 用于处理来自服务器的压缩的内容
- server: HttpServerCodec
- server: HttpContentCompressor 用于压缩来自 client 支持的 HttpContentCompressor
压缩与依赖
注意,Java 6或者更早版本,如果要压缩数据,需要添加 jzlib 到 classpath
|
|
使用 HTTPS
启用 HTTPS,只需添加 SslHandler
代码2.4 使用 HTTPS
|
|
- 添加 SslHandler 到 pipeline 来启用 HTTPS
- client: 添加 HttpClientCodec
- server: 添加 HttpServerCodec ,如果是 server 模式的话
上面的代码就是一个很好的例子,解释了 Netty 的架构是如何让“重用”变成了“杠杆”。我们可以添加一个新的功能,甚至是一样重要的加密支持,几乎没有工作量,只需添加一个ChannelHandler 到 ChannelPipeline。
WebSocket
HTTP 是不错的协议,但是如果需要实时发布信息怎么做?有个做法就是客户端一直轮询请求服务器,这种方式虽然可以达到目的,但是其缺点很多,也不是优秀的解决方案,为了解决这个问题,便出现了 WebSocket。
WebSocket 允许数据双向传输,而不需要请求-响应模式。简单的说, 一个WebSocket 提供一个 TCP 连接两个方向的交通。结合 WebSocket API 它提供了一个替代 HTTP 轮询双向通信从页面到远程服务器。也就是说,WebSocket 提供真正的双向客户机和服务器之间的数据交换。
代码2.5 在服务器上支持WebSocket
|
|
- 添加 HttpObjectAggregator 用于提供在握手时聚合 HttpRequest
- 添加 WebSocketServerProtocolHandler 用于处理色好给你寄握手如果请求是发送到”/websocket.” 端点,当升级完成后,它将会处理Ping, Pong 和 Close 帧
- TextFrameHandler 将会处理 TextWebSocketFrames
- BinaryFrameHandler 将会处理 BinaryWebSocketFrames
- ContinuationFrameHandler 将会处理ContinuationWebSocketFrames
加密 WebSocket 只需插入 SslHandler 到作为 pipline 第一个 ChannelHandler