资讯详情

netty系列之:让TLS支持http2

文章目录

  • 简介
  • TLS的扩展协议NPN和ALPN
  • SslProvider
  • ApplicationProtocolConfig
  • 构建SslContext
  • ProtocolNegotiationHandler
  • 总结

简介

尽管如此HTTP协议不强制使用HTTPS,但对于大多数浏览器来说,如果要使用的话HTTP2的话,则必须使用HTTPS,所以我们需要知道如何理解netty的TLS中支持http2。

TLS的扩展协议NPN和ALPN

HTTP2协议是从spdy协议已经发展起来,无论是spdy还是http2都为了能在HTTPS在环境下工作,发展TLS扩展协议。

他们分别叫NPN(Next Protocol Negotiation) 和 ALPN (Application Layer Protocol Negotiation) 。

他们规定了TLS协议握手后,客户端与服务器端达成应用数据通信协议。ALPN当客户端首次与服务器端握手时,可以列出客户端支持的应用层数据协议。可以直接选择服务器端,可以比较NPN少一个交互过程,更好。

那么spdy和http分别支持的协议有哪些?

netty提供了一个ApplicationProtocolNames其中定义了相应的协议,其中ALPN对应了http2和http1.1,而sydy对应了spdy/1,spdy/2,spdy/3:

     /**      * HTTP version 2      */     public static final String HTTP_2 = "h2";      /**      * {@code "http/1.1"}: HTTP version 1.1      */     public static final String HTTP_1_1 = "http/1.1";      /**      * {@code "spdy/3.1"}: SPDY version 3.1      */     public static final String SPDY_3_1 = "spdy/3.1";      /**      * {@code "spdy/3"}: SPDY version 3      */     public static final String SPDY_3 = "spdy/3";      /**      * {@code "spdy/2"}: SPDY version 2      */     public static final String SPDY_2 = "spdy/2";      /**      * {@code "spdy/1"}: SPDY version 1      */     public static final String SPDY_1 = "spdy/1"; 

SslProvider

目前来说,netty中有两种SSL一是实现JDK,一种是OPENSSL,实现方式不同TLS协议扩展的支持也不同。它提供了一个isAlpnSupported方法,根据传入provider判断是否支持不同的判断ALPN。

    public static boolean isAlpnSupported(final SslProvider provider) {         switch (provider) {             case JDK:                 return JdkAlpnApplicationProtocolNegotiator.isAlpnSupported();             case OPENSSL:             case OPENSSL_REFCNT:                 return OpenSsl.isAlpnSupported();             default:                 throw new Error("Unknown SslProvider: "   provider);         }     } 

如果你使用的是JDK8.运行后,可能会得到以下错误提示:

ALPN is only supported in Java9 or if you use conscrypt as your provider or have the jetty alpn stuff on the class path. 

也就是说,如果是用的话JDK作为默认的SSL provider不支持ALPN必须升级到java9.

按提示添加conscrypt到classpath中:

        <dependency>             <groupId>org.conscrypt</groupId>             <artifactId>conscrypt-openjdk-uber</artifactId>             <version>2.5.2</version>         </dependency> 

以下错误将在运行后得到:

Unable to wrap SSLEngine of type 'sun.security.ssl.SSLEngineImpl' 

怎么办?答案是用的。Open SSL,还需添加:

        <dependency>             <groupId>io.netty</groupId>             <artifactId>netty-tcnative-boringssl-static</artifactId>             <version>2.0.40.Final</version>         </dependency> 

测试后,完美执行。

ApplicationProtocolConfig

ApplicationProtocolConfig是netty提供传递SSLEngine协议配置类属性:

    private final List<String> supportedProtocols;     private final Protocol protocol;     private final SelectorFailureBehavior selectorBehavior;     private final SelectedListenerFailureBehavior selectedBehavior; 

supportedProtocols支持上述数据传输协议HTTP2,HTTP1.1或者spdy/1,spdy/2,spdy/3等。

protocol是TLS的扩展协议,像ALPN或者NPN等。

selectorBehavior在选择协议时,有三种方式:

FATAL_ALERT: 如果应用程序协议的节点没有找到匹配项,握手将失败。 NO_ADVERTISE: 如果应用程序协议的节点没有找到匹配项,它将假装不支持握手 TLS 扩展。 CHOOSE_MY_LAST_PROTOCOL: 如果选择应用程序协议的节点没有找到匹配项,则将使用上次推荐的协议。

selectedBehavior是通知被选择的协议之后的表现方式,也有3种方式:

ACCEPT: 如果节点不支持对方节点选择的应用协议,则节点默认不支持TLS扩展,然后继续握手。 FATAL_ALERT: 如果节点不支持对方节点选择的应用协议,则握手失败。 CHOOSE_MY_LAST_PROTOCOL: 如果节点不支持对方节点选择的应用程序协议,则将使用上次推荐的协议。

构建SslContext

有了provider,ApplicationProtocolConfig 之后,就可以建了SslContext是的。首先创建SSL provider:

 SslProvider provider =  SslProvider.isAlpnSupported(SslProvider.OPENSSL)  ? SslProvider.OPENSSL : SslProvider.JDK;             

默认情况下使用JDK作为ssl provider,假如你用的是OpenSSL使用它OpenSSL。

我们使用SslContextBuilder.forServer来创建SslContext,这种方法需要引入certificate和privateKey,为了简单起见,我们使用自签名SelfSignedCertificate:

SelfSignedCertificate ssc = new SelfSignedCertificate(); sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();

还可以为其设置sslProvider,ciphers和applicationProtocolConfig等信息:

sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
                .sslProvider(provider)
                //支持的cipher
                .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                .applicationProtocolConfig(new ApplicationProtocolConfig(
                    Protocol.ALPN,
                    // 目前 OpenSsl 和 JDK providers只支持NO_ADVERTISE
                    SelectorFailureBehavior.NO_ADVERTISE,
                    // 目前 OpenSsl 和 JDK providers只支持ACCEPT
                    SelectedListenerFailureBehavior.ACCEPT,
                    ApplicationProtocolNames.HTTP_2,
                    ApplicationProtocolNames.HTTP_1_1))
                .build();

ProtocolNegotiationHandler

最后,我们需要根据协商使用的不同协议,进行不同的处理。netty提供了一个ApplicationProtocolNegotiationHandler,自定义的话,只需要继承该类即可,比如,我们根据protocol的名称不同,来分别处理HTTP1和HTTP2请求:

   public class MyNegotiationHandler extends ApplicationProtocolNegotiationHandler {
       public MyNegotiationHandler() {
           super(ApplicationProtocolNames.HTTP_1_1);
       }
  
       protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
           if (ApplicationProtocolNames.HTTP_2.equals(protocol) {
               configureHttp2(ctx);
           } else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
               configureHttp1(ctx);
           } else {
               throw new IllegalStateException("unknown protocol: " + protocol);
           }
       }
   }

然后将其加入到ChannelPipeline中即可:

   public class MyInitializer extends ChannelInitializer<Channel> {
       private final SslContext sslCtx;
  
       public MyInitializer(SslContext sslCtx) {
           this.sslCtx = sslCtx;
       }
  
       protected void initChannel(Channel ch) {
           ChannelPipeline p = ch.pipeline();
           p.addLast(sslCtx.newHandler(...)); // Adds SslHandler
           p.addLast(new MyNegotiationHandler());
       }
   }

总结

以上就是在netty中配置TLS支持HTTP2的完整流程了。

本文的例子可以参考:learn-netty4

本文已收录于 http://www.flydean.com/26-netty-secure-http2/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

标签: 肖特基二极管ssc53l

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台