栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Netty搭建Http2服务端并支持TLS传输加密

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Netty搭建Http2服务端并支持TLS传输加密

Netty搭建Http2服务端并支持TLS传输加密
@Slf4j
public class SslUtil {

    public static SslContext sslContext() {
        SslProvider provider = SslProvider.isAlpnSupported(SslProvider.OPENSSL) ? SslProvider.OPENSSL : SslProvider.JDK;

        log.info("provider:{}", provider);

        SelfSignedCertificate ssc = null;
        try {
            ssc = new SelfSignedCertificate();
        } catch (CertificateException e) {
            throw new RuntimeException(e);
        }

        ApplicationProtocolConfig applicationProtocolConfig = new ApplicationProtocolConfig(
                ApplicationProtocolConfig.Protocol.ALPN,
                ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
                ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
                ApplicationProtocolNames.HTTP_2,
                ApplicationProtocolNames.HTTP_1_1);

        SslContext sslContext = null;
        try {
            sslContext = SslContextBuilder
                    .forServer(ssc.certificate(), ssc.privateKey())
                    .sslProvider(provider)
                    .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                    .applicationProtocolConfig(applicationProtocolConfig)
                    .build();
        } catch (SSLException e) {
            throw new RuntimeException(e);
        }

        return sslContext;
    }

}

注意:io.netty.handler.ssl.util.SelfSignedCertificate 只用于测试,不要用于生产环境!!!

public class Http2ServerResponseHandler extends ChannelDuplexHandler {
    static final ByteBuf RESPONSE_BYTES = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8));
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof Http2Headersframe) {
            Http2Headersframe msgHeader = (Http2Headersframe) msg;
            if (msgHeader.isEndStream()) {
                ByteBuf content = ctx.alloc().buffer();
                content.writeBytes(RESPONSE_BYTES.duplicate());

                Http2Headers headers = new DefaultHttp2Headers().status(HttpResponseStatus.OK.codeAsText());
                ctx.write(new DefaultHttp2Headersframe(headers).stream(msgHeader.stream()));
                ctx.write(new DefaultHttp2Dataframe(content, true).stream(msgHeader.stream()));
            }
        } else {
            super.channelRead(ctx, msg);
        }
    }
    
}
@Slf4j
public class Http2Server {

    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workerGroup;

    public synchronized void start(int port) {
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new Http2ServerInitializer());
            //
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            log.info("http2 server started on port:{}", port);
        } catch (Exception e) {
            log.error("Http2ServerBootstrap-->", e);
            close();
        }
    }

    public static class Http2ServerInitializer extends ChannelInitializer {

        @Override
        protected void initChannel(SocketChannel channel) throws Exception {
            ChannelPipeline pipeline = channel.pipeline();
            SslHandler sslHandler = SslUtil.sslContext().newHandler(channel.alloc());
            pipeline.addLast("sslHandler", sslHandler);
            pipeline.addLast("applicationProtocolNegotiationHandler", new MyAppProtocolNegotiationHandler());
        }
    }
    //根据请求的协议添加对应的处理器
    public static class MyAppProtocolNegotiationHandler extends ApplicationProtocolNegotiationHandler {

        public MyAppProtocolNegotiationHandler() {
            super(ApplicationProtocolNames.HTTP_2);
        }

        protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
            if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
                Http2frameCodec http2frameCodec = Http2frameCodecBuilder.forServer().build();

                ctx.pipeline().addLast(http2frameCodec);
                ctx.pipeline().addLast(new Http2ServerResponseHandler());

            } else {
                throw new IllegalStateException("Protocol: " + protocol + " not supported");
            }
        }
    }

    public synchronized void close() {
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
        log.info("destroy netty server thread");
    }

}
public static void main(String[] args) {
    Http2Server http2Server = new Http2Server();
    http2Server.start(8443);
}

测试http2服务

curl -k -v --http2 https://127.0.0.1:8443

测试http1.1服务

curl -k -v --http1.1 https://127.0.0.1:8443
使用 jdk 提供的SslProvider

使用jdk提供的SslProvider,但是jdk8不支持ALPN,出现如下异常,只能使用高版本的jdk。

示例使用的是jdk17可以支持ALPN,但是运行时会出现如下异常

Caused by: java.security.cert.CertificateException: No provider succeeded to generate a self-signed certificate. See debug log for the root cause.
	at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:249) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:166) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:115) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:90) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	at com.bruce.netty.http2.handler.SslUtil.sslContext(SslUtil.java:24) ~[classes/:na]
	... 21 common frames omitted
	Suppressed: java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider
		at io.netty.handler.ssl.util.BouncyCastleSelfSignedCertGenerator.(BouncyCastleSelfSignedCertGenerator.java:43) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
		at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:240) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
		... 25 common frames omitted
	Caused by: java.lang.ClassNotFoundException: org.bouncycastle.jce.provider.BouncyCastleProvider
		at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
		at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
		at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
		... 27 common frames omitted
Caused by: java.lang.IllegalAccessError: class io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator (in unnamed module @0x5e316c74) cannot access class sun.security.x509.X509CertInfo (in module java.base) because module java.base does not export sun.security.x509 to unnamed module @0x5e316c74
	at io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator.generate(OpenJdkSelfSignedCertGenerator.java:52) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:246) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	... 25 common frames omitted

该问题乍一看以为是缺少org.bouncycastle.jce.provider.BouncyCastleProvider导致的异常,当添加了如下依赖后确实也可以解决问题。


    org.bouncycastle
    bcpkix-jdk15on
    1.70

但实际原因并不是缺少该依赖导致的,看最后一个Caused by:
because module java.base does not export sun.security.x509 to unnamed module @0x5e316c74

实际原因:jdk17因为模块化并没有在 java.base中导出sun.security.x509包,没有权限访问sun.security.x509.X509CertInfo导致无法实例化。

解决办法:不需要org.bouncycastle:bcpkix-jdk15on依赖,在启动时添加如下参数即可

--add-opens java.base/sun.security.x509=ALL-UNNAMED
使用OPENSSL提供的SslProvider

添加如下依赖依赖,OPENSSL不需要依赖jdk提供了ALPN支持,因此在jdk8,jdk17上同样可以运行。


    io.netty
    netty-tcnative-boringssl-static

当切换到jdk17时,再次执行则会出现上面所提到的异常,具体原因及解决方案上面已经说明

Caused by: java.lang.IllegalAccessError: class io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator (in unnamed module @0x5e316c74) cannot access class sun.security.x509.X509CertInfo (in module java.base) because module java.base does not export sun.security.x509 to unnamed module @0x5e316c74
	at io.netty.handler.ssl.util.OpenJdkSelfSignedCertGenerator.generate(OpenJdkSelfSignedCertGenerator.java:52) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	at io.netty.handler.ssl.util.SelfSignedCertificate.(SelfSignedCertificate.java:246) ~[netty-handler-4.1.70.Final.jar:4.1.70.Final]
	... 25 common frames omitted

但为什么加了org.bouncycastle:bcpkix-jdk15on依赖同样可以解决问题呢?
源码见:io.netty.handler.ssl.util.SelfSignedCertificate#SelfSignedCertificate(String, java.security.SecureRandom, int, Date, Date, String)
原因是Netty优先使用的使用Bouncy Castle生成自签名证书。当不存在时再使用OpenJDK提供的sun.security.x509生成自签名证书。所以使用Bouncy Castle之后就不会走到下面逻辑,不会访问创建X509CertInfo对象,所以正常执行.

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/657703.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号