netty实现SSL 双向认证与用jmeter测试

 有时RestApi接口需要实现双向认证,验证客户端请求的合法来源,这里用netty实现了https请求的双向认证

首先ide里生成一个maven项目,pom.xml加入netty依懒包

 
 <dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.20.Final</version>
</dependency>

 一、   openssl生成证书

  sslauth建立目录security存放证书,

注意生成证书common-name参数为localhost,用于本地测试,“A challenge password”可不输

 1. 建立root CA

  security目录下执行下列命令,建立要证书,用来做签名CA证书

  • openssl genrsa -out rootCA.key 2048
  •  openssl req -x509 -new -nodes -key rootCA.key -days   1024 -out rootCA.pem

 2. 建立服务端证书

  security建立service目录,存放服务端证书

  • openssl genrsa -out service/service.key 2048
  • openssl req -new -key service/service.key -out service/service.csr
  • openssl x509 -req -in service/service.csr -CA ./rootCA.pem -CAkey ./rootCA.key -CAcreateserial -out service/service.crt -days 500 -sha256
  • openssl pkcs8 -topk8 -inform PEM -outform PEM -in service/service.key -out service/service.pkcs8.key -nocrypt

3. 建立客户端证书

   security建立client目录,存放客户端证书

  • openssl genrsa -out client/client.key 2048
  • openssl req -new -key client/client.key -out client/client.csr
  • openssl x509 -req -in client/client.csr -CA ./rootCA.pem -CAkey ./rootCA.key -CAcreateserial -out client/client.crt -days 500 -sha256
  • openssl pkcs8 -topk8 -inform PEM -outform PEM -in client/client.key -out client/client.pkcs8.key -nocrypt

二、   建立netty https

 1. 建立DefaultRequestHandler

  用来处理用户请求,输出HelloWorld

public class DefaultRequestHandler extends ChannelInboundHandlerAdapter {
    private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };

    private static final AsciiString CONTENT_TYPE = AsciiString.cached("Content-Type");
    private static final AsciiString CONTENT_LENGTH = AsciiString.cached("Content-Length");
    private static final AsciiString CONNECTION = AsciiString.cached("Connection");
    private static final AsciiString KEEP_ALIVE = AsciiString.cached("keep-alive");

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof HttpRequest) {
            HttpRequest req = (HttpRequest) msg;

            boolean keepAlive = HttpUtil.isKeepAlive(req);
            FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT));
            response.headers().set(CONTENT_TYPE, "text/plain");
            response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());

            if (!keepAlive) {
                ctx.write(response).addListener(ChannelFutureListener.CLOSE);
            } else {
                response.headers().set(CONNECTION, KEEP_ALIVE);
                ctx.write(response);
            }
        }
    }

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

 

2. 建立PipelineInitializer

 初始化netty pipelline,加入ssl handler

public class PipelineInitializer extends ChannelInitializer<SocketChannel> {
    private final SslContext sslCtx;

    public PipelineInitializer(SslContext sslCtx) {
        this.sslCtx = sslCtx;
    }

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (sslCtx != null) {
            pipeline.addLast(sslCtx.newHandler(ch.alloc()));
        }
        pipeline.addLast("decoder", new HttpRequestDecoder());
        pipeline.addLast("encoder", new HttpResponseEncoder());
        pipeline.addLast("aggregator", new HttpObjectAggregator(20480));
        pipeline.addLast("chunkWriter", new ChunkedWriteHandler());
        pipeline.addLast(new DefaultRequestHandler());
    }
}

 

3. 建立Server

       应用主类,启动应用,接收4个参数:应用的端口、服务端证书(service/ service.crt)、服务端私钥(service/ service.pkcs8.key)、根证书(rootCA.pem)

         public final class Server {
    static int PORT = 8443;
    public static void main(String[] args) throws Exception {
        if (args.length < 4) {
            System.err.println("args=: port cert privateKey caKey");
            System.exit(1);
        }
        final SslContext sslCtx;
        PORT = Integer.parseInt(args[0]);
        File cert = new File(args[1]);
        File priKey = new File(args[2]);
        File caKey = new File(args[3]);
        sslCtx = SslContextBuilder.forServer(cert, priKey)
                .clientAuth(ClientAuth.REQUIRE)
                .trustManager(caKey).build();
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.option(ChannelOption.SO_BACKLOG, 1024);
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new PipelineInitializer(sslCtx));
            Channel ch = b.bind(PORT).sync().channel();
            System.err.println("https://127.0.0.1:" + PORT + '/');
            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 

三、   jmeter测试

1. 启动jmeter

  • 导出客户端证书为p12格式,导出密码:123456

      openssl pkcs12 -export -cacerts -inkey client/client.key -in client/client.crt -out client/client.p12

  • keytool导入根证书,密码:123456

      keytool -import -file ./rootCA.pem -keystore client/ca.jks

  •  启动jmeter

 bin/jmeter -Djavax.net.ssl.trustStore=security/ca.store -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.keyStore=security/client/client.p12

2. jmeter测试脚本

新建测试计划=》建立线程组=》建立HTTP请求(路径:https://localhost:8443)=》察看结果树

3. 启动jmeter测试脚本


以下是一个使用Netty建立SSL双向认证的服务器和客户端的简单示例: 首先,确保你已经安装了Java和Maven,并且已经创建了一个Maven项目。 在pom.xml文件中添加以下依赖项: ```xml <dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.65.Final</version> </dependency> </dependencies> ``` 接下来,创建一个Server类来实现服务器端的逻辑: ```java import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.util.SelfSignedCertificate; import javax.net.ssl.SSLEngine; import java.security.cert.CertificateException; public class Server { private static final int PORT = 8888; public static void main(String[] args) throws Exception { SelfSignedCertificate cert = new SelfSignedCertificate(); SslContext sslContext = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()).build(); EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); SSLEngine engine = sslContext.newEngine(ch.alloc()); engine.setUseClientMode(false); engine.setNeedClientAuth(true); p.addLast(new SslHandler(engine)); p.addLast(new ServerHandler()); } }); ChannelFuture f = b.bind(PORT).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 然后,创建一个ServerHandler类来处理服务器端的请求: ```java import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; try { while (buf.isReadable()) { System.out.print((char) buf.readByte()); System.out.flush(); } } finally { buf.release(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` 最后,创建一个Client类来实现客户端的逻辑: ```java import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import javax.net.ssl.SSLEngine; public class Client { private static final String HOST = "localhost"; private static final int PORT = 8888; public static void main(String[] args) throws Exception { SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build(); EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); SSLEngine engine = sslContext.newEngine(ch.alloc(), HOST, PORT); engine.setUseClientMode(true); p.addLast(new SslHandler(engine)); p.addLast(new ClientHandler()); } }); ChannelFuture f = b.connect(HOST, PORT).sync(); f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); } } } ``` 然后,创建一个ClientHandler类来处理客户端的请求: ```java import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class ClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { String message = "Hello, server!"; ByteBuf buf = ctx.alloc().buffer(message.length()); buf.writeBytes(message.getBytes()); ctx.writeAndFlush(buf); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf = (ByteBuf) msg; try { while (buf.isReadable()) { System.out.print((char) buf.readByte()); System.out.flush(); } } finally { buf.release(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } ``` 以上示例代码演示了如何使用Netty建立一个基于SSL双向认证的服务器和客户端。服务器端使用自签名证书,客户端信任任何服务器端的证书。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值