1. 加载ssl证书的工具类
public class SslUtil { private static volatile SSLContext sslContext = null;
// type是PKCS12、path是pfx文件路径、password是pfx对应的密码
public static SSLContext createSSLContext(String type ,String path ,String password) throws Exception { if(null == sslContext){ synchronized (SslUtil.class) { if(null == sslContext){ // 支持JKS、PKCS12(我们项目中用的是阿里云免费申请的证书,下载tomcat解压后的pfx文件,对应PKCS12) KeyStore ks = KeyStore.getInstance(type); // 证书存放地址 InputStream ksInputStream = new FileInputStream(path); ks.load(ksInputStream, password.toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, password.toCharArray()); sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, null); } } } return sslContext; } }
2. 将SslHandler放在第一个
bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 128) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // SSL处理器 SSLContext sslContext = SslUtil.createSSLContext(BaseGlobal.getCertType(), BaseGlobal.getCertPath(), BaseGlobal.getCertPassword()); SSLEngine sslEngine = sslContext.createSSLEngine(); sslEngine.setNeedClientAuth(false); sslEngine.setUseClientMode(false); pipeline.addLast("sslHandler", new SslHandler(sslEngine)); pipeline.addLast("idleStateHandler", new IdleStateHandler(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds)); ... } });
3. 因为我们项目的特殊性,还需要同时支持TCP、WS协议,所以使用多线程加载两个NettyServer
@EnableCaching @EnableAutoConfiguration @SpringBootApplication(scanBasePackages = "xxx") @MapperScan(basePackages = "xxx") @EnableAsync public class V3xboxApplication { public static void main(String[] args) { SpringApplication.run(V3xboxApplication.class, args); // 启动服务端(客户端TCP连接) // 使用线程启动,是因为Netty的sync方法会阻塞线程 // 此处不使用线程池的原因是这里只需要一个线程,不存在线程的频繁销毁创建 NettyServerThread nettyServerThread = new NettyServerThread(); Thread thread1 = new Thread(nettyServerThread); thread1.start(); // 如果设置wss端口的话,则启动wss处理服务器 if(StringUtil.isNotEmpty(BaseGlobal.getWssPort())) { NettyWssServerThread sslServerThread = new NettyWssServerThread(); Thread thread2 = new Thread(sslServerThread); thread2.start(); } } }
4. 因为我们需要在程序动态判断WS还是WSS,所以在nginx的proxy配置了,这样后台就可以识别客户端是https还是http
proxy_set_header scheme $scheme;