http代理服务器(三)fiddler【重点】
0 host 头
https://www.jianshu.com/p/5332f470e33c
https://zhuanlan.zhihu.com/p/33264232
1 将19年的程序24netty(二十)http代理服务器【重点】再拿出来居然能直接用
整理一下
package com.jds.test.httpproxy.miniserver; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.http.*; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; public class HttpServerJob implements Runnable { final EventLoopGroup CLIENT_BOSS_LOOP_GROUP = new NioEventLoopGroup(1); final EventLoopGroup CLIENT_WORKER_LOOP_GROUP = new NioEventLoopGroup(4); public static void main(String[] args) throws Exception { new Thread(new HttpServerJob(8990, "https://xxx/", "xxx")).start(); new Thread(new HttpServerJob(8990, "https://xxx/", "xxx")).start(); new Thread(new HttpServerJob(8990, "https://xxx/", "xxx")).start(); } public HttpServerJob(int port, String url, String auth) { this.port = port; this.url = url; this.auth = auth; } private int port; private String url; private ReqQueue reqQueue = new ReqQueue(); private String auth; @Override public void run() { startHttpServer(); } public void startHttpServer() { try { ServerBootstrap bs = new ServerBootstrap(); bs.group(CLIENT_BOSS_LOOP_GROUP, CLIENT_WORKER_LOOP_GROUP); bs.channel(NioServerSocketChannel.class); bs.childHandler(new HttpServerInitializer()); ChannelFuture future = bs.bind(this.port).sync(); System.out.println("http server start at " + port); reqQueue.startConsumer(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { CLIENT_BOSS_LOOP_GROUP.shutdownGracefully(); CLIENT_WORKER_LOOP_GROUP.shutdownGracefully(); } } private class HttpServerInitializer extends ChannelInitializer<SocketChannel> { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE)); pipeline.addLast(new BodyToResponseEncoder()); pipeline.addLast(new RequestToBodyDecoder()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); super.exceptionCaught(ctx, cause); } } public static class ReqQueue { private BlockingQueue<OriginHttp> arrayBlockingQueue = new LinkedBlockingDeque<OriginHttp>(100); public void startConsumer() { for(int i=0; i<10; ++i) { new Thread(new Customer()).start(); } } public void put(OriginHttp originHttp) { try { arrayBlockingQueue.put(originHttp); } catch (InterruptedException e) { e.printStackTrace(); } } private class Customer implements Runnable { @Override public void run() { try { while (true){ OriginHttp originHttp = arrayBlockingQueue.take(); ProxySender.send(originHttp, originHttp.getUrl()); Thread.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } } } } private class RequestToBodyDecoder extends MessageToMessageDecoder<FullHttpRequest> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("active"); super.channelActive(ctx); } @Override protected void decode(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest, List<Object> list) throws Exception { HttpMethod httpMethod = fullHttpRequest.getMethod(); String uri = fullHttpRequest.getUri(); HttpHeaders httpHeaders = fullHttpRequest.headers(); Map<String, String> map = new HashMap<>(); for (Map.Entry<String, String> entry : httpHeaders.entries()) { map.put(entry.getKey(), entry.getValue()); } ByteBuf msg = fullHttpRequest.content(); byte[] bs = new byte[msg.readableBytes()]; msg.readBytes(bs); OriginHttp originHttp = new OriginHttp(httpMethod.name(), uri, map, bs, channelHandlerContext); originHttp.setUrl(url); originHttp.setAuth(auth); reqQueue.put(originHttp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); super.exceptionCaught(ctx, cause); } } public static class BodyToResponseEncoder extends MessageToMessageEncoder<ResHttp> { @Override protected void encode(ChannelHandlerContext channelHandlerContext, ResHttp resHttp, List<Object> list) throws Exception { FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(resHttp.getRet()), Unpooled.wrappedBuffer(resHttp.getBody())); Map<String, String> headers = resHttp.getHeaders(); for(Map.Entry<String, String> entry : headers.entrySet()) { response.headers().set(entry.getKey(), entry.getValue()); if(entry.getKey().equals("Set-Cookie")) { response.headers().set(entry.getKey(), entry.getValue().replace("Secure", "").replace("HttpOnly", "")); } } response.headers().remove("X-Frame-Options"); response.headers().set("testproxy", "hhh"); /** * one of content_length and chunked in response header neccesary in http long connections */ if(!response.headers().contains("Transfer-Encoding") && !"chunked".equals(response.headers().get("Transfer-Encoding"))) response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); // response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); // response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); // response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS, HttpHeaders.Names.CONTENT_TYPE); list.add(response); } } public static class OriginHttp { private String url; private String method; private String uri; private Map<String, String> headers; private byte [] body; ChannelHandlerContext context; private String auth; public OriginHttp(String method, String uri, Map<String, String> headers, byte [] body, ChannelHandlerContext context) { this.method = method; this.uri = uri; this.headers = headers; this.body = body; this.context = context; } public String getMethod() { return method; } public String getUri() { return uri; } public Map<String, String> getHeaders() { return headers; } public byte[] getBody() { return body; } public ChannelHandlerContext getContext() { return context; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getAuth() { return auth; } public void setAuth(String auth) { this.auth = auth; } } private static class ProxySender { public static void send(OriginHttp originHttp, String dir) { String method = originHttp.getMethod(); String uri = originHttp.getUri(); String url = new StringBuilder(dir).append(uri).toString(); System.out.println(url); HttpUriRequest httpUriRequest = null; if ("GET".equals(method)) { httpUriRequest = new HttpGet(url); } else if ("POST".equals(method)) { httpUriRequest = new HttpPost(url); HttpPost httpPost = (HttpPost) httpUriRequest; try { String st = new String(originHttp.getBody()); httpPost.setEntity(new StringEntity(st)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } Map<String, String> headers = originHttp.getHeaders(); for (Map.Entry<String, String> entry : headers.entrySet()) { String val = entry.getValue(); httpUriRequest.addHeader(entry.getKey(), val); } // httpUriRequest.addHeader("Cookie", cookie); httpUriRequest.removeHeaders("Content-Length"); httpUriRequest.removeHeaders("Origin"); httpUriRequest.removeHeaders("Referer"); httpUriRequest.removeHeaders("Host"); httpUriRequest.addHeader("Origin", dir); httpUriRequest.addHeader("Referer", dir); httpUriRequest.addHeader("sm_user", originHttp.getAuth()); // httpUriRequest.addHeader("Host", dir); CloseableHttpClient httpClient = HttpClientFactory.createSSLClientDefault(); org.apache.http.HttpResponse response = null; try { response = httpClient.execute(httpUriRequest); Map<String, String> map = new HashMap<>(); Header[] headersRes = response.getAllHeaders(); for (Header header : headersRes) { map.put(header.getName(), header.getValue()); } int ret = response.getStatusLine().getStatusCode(); HttpEntity responseEntity = response.getEntity(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); if(responseEntity != null) { InputStream inputStream = responseEntity.getContent(); byte[] bytes = new byte[1024]; int i = 0; while ((i = inputStream.read(bytes)) != -1) { byteArrayOutputStream.write(bytes, 0, i); } } ResHttp resHttp = new ResHttp(ret, map, byteArrayOutputStream.toByteArray()); ChannelFuture channelFuture = originHttp.getContext().writeAndFlush(resHttp); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { /** * to use http short connections by this line */ // future.channel().close(); if (!future.isSuccess()) { future.cause().printStackTrace(); future.channel().close(); } } }); } catch (Exception e) { e.printStackTrace(); } finally { } } } private static class HttpClientFactory { public static CloseableHttpClient createSSLClientDefault() { try { SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE; SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); return HttpClients.custom().setSSLSocketFactory(sslsf).build(); } catch (Exception e) { e.printStackTrace(); } return HttpClients.createDefault(); } } private static class ResHttp { private int ret; private Map<String, String> headers; private byte [] body; public ResHttp(int ret, Map headers, byte [] body) { this.ret = ret; this.headers = headers; this.body = body; } public int getRet() { return ret; } public Map<String, String> getHeaders() { return headers; } public byte[] getBody() { return body; } } }
1.1 发现比不用代理快,因为chrome有6个连接限制(这句话是错的,因为浏览头在chrome,mq线程未必有用)
1.2 局域网访问出问题,session任意时刻都过期
判断set-cookie 头出问题,跟踪chrome network,发现 response的set-cookie头旁边:
This attempt to set a cookie via a Set-Cookie header was blocked because it had the Secure attribute but was not recerved over a secure connection
https://blog.csdn.net/gusijin/article/details/120541938
Add yellow:
private static class BodyToResponseEncoder extends MessageToMessageEncoder<ResHttp> {
@Override
protected void encode(ChannelHandlerContext
channelHandlerContext, ResHttp resHttp, List<Object> list) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(resHttp.getRet()),
Unpooled.wrappedBuffer(resHttp.getBody()));
Map<String, String> headers = resHttp.getHeaders();
for(Map.Entry<String,
String> entry : headers.entrySet()) {
response.headers().set(entry.getKey(), entry.getValue());
if(entry.getKey().equals("Set-Cookie"))
{
response.headers().set(entry.getKey(),
entry.getValue().replace("Secure", "").replace("HttpOnly", ""));
}
}
response.headers().remove("X-Frame-Options");
/**
* one
of content_length and chunked in response header neccesary in http long
connections
*/
if(!response.headers().contains("Transfer-Encoding") && !"chunked".equals(response.headers().get("Transfer-Encoding")))
response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
response.content().readableBytes());
//
response.headers().set(HttpHeaders.Names.CONNECTION,
HttpHeaders.Values.KEEP_ALIVE);
//
response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN,
"*");
//
response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS,
HttpHeaders.Names.CONTENT_TYPE);
list.add(response);
}
}
2 开发fiddler,透明代理
https://blog.csdn.net/dotalee/article/details/77838676
https://www.jianshu.com/p/aaa211c11a27
package com.jds.test.httpproxy.miniserver; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.*; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; public class ProxyNoHttpsDecode { public static void main(String []jj) { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(2); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .option(ChannelOption.TCP_NODELAY, true) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast("httpCodec",new HttpServerCodec()); ch.pipeline().addLast("httpObject",new HttpObjectAggregator(65536)); ch.pipeline().addLast("serverHandle",new HttpProxyServerHandle()); } }); ChannelFuture f = b .bind(1999) .sync(); f.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static class HttpProxyServerHandle extends ChannelInboundHandlerAdapter { private ChannelFuture cf; private String host; private int port; @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { if (msg instanceof FullHttpRequest) { FullHttpRequest request = (FullHttpRequest) msg; String host = request.headers().get("host"); String[] temp = host.split(":"); int port = 80; if (temp.length > 1) { port = Integer.parseInt(temp[1]); } else { if (request.getUri().indexOf("https") == 0) { port = 443; } } this.host = temp[0]; this.port = port; if ("CONNECT".equalsIgnoreCase(request.getMethod().name())) {//HTTPS建立代理握手 HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); ctx.writeAndFlush(response); System.out.println("--------------------cc----------------------"); ctx.pipeline().remove("httpCodec"); ctx.pipeline().remove("httpObject"); return; } Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) .channel(ctx.channel().getClass()) .handler(new HttpProxyInitializer(ctx.channel())); ChannelFuture cf = bootstrap.connect(temp[0], port); cf.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { future.channel().writeAndFlush(msg); } else { ctx.channel().close(); } } }); // ChannelFuture cf = bootstrap.connect(temp[0], port).sync(); // cf.channel().writeAndFlush(request); } else { // https 只转发数据,不做处理 if (cf == null) { System.out.println("------------------------------------------"); Bootstrap bootstrap = new Bootstrap(); bootstrap.group(ctx.channel().eventLoop()) .channel(ctx.channel().getClass()) .handler(new ChannelInitializer() { @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx0, Object msg) throws Exception { ctx.channel().writeAndFlush(msg); } }); } }); cf = bootstrap.connect(host, port); cf.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { future.channel().writeAndFlush(msg); } else { ctx.channel().close(); } } }); } else { cf.channel().writeAndFlush(msg); } } } } public static class HttpProxyInitializer extends ChannelInitializer{ private Channel clientChannel; public HttpProxyInitializer(Channel clientChannel) { this.clientChannel = clientChannel; } @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline().addLast(new HttpClientCodec()); ch.pipeline().addLast(new HttpObjectAggregator(6553600)); ch.pipeline().addLast(new HttpProxyClientHandle(clientChannel)); } } public static class HttpProxyClientHandle extends ChannelInboundHandlerAdapter { private Channel clientChannel; public HttpProxyClientHandle(Channel clientChannel) { this.clientChannel = clientChannel; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { FullHttpResponse response = (FullHttpResponse) msg; response.headers().add("test","from proxy"); clientChannel.writeAndFlush(msg); } } }
目标服务器取首次Connect http request header 的 host
<dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.0.29.Final</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.8</version> </dependency> </dependencies>
3 明文代理
3.0 原理:
fiddler原理+fiddler为什么抓chrome而不能抓curl和httpclient?fiddler为什么能篡改报文?
3.1 先跟着 https://www.cnblogs.com/javalinux/p/16136628.html(netty https hsc123)吧netty https搭起来
keytool -genkey -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass hsc123 -storepass hsc123 -keystore local.jks
3.2 结合2的CONNECT METHOD,搭建https代理
package com.jds.test.httpproxy.miniserver; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.ssl.SslHandler; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import java.io.FileInputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.util.HashMap; import java.util.List; import java.util.Map; import io.netty.handler.codec.http.*; public class ProxyHttpsDecode { private static HttpServerJob.ReqQueue reqQueue = new HttpServerJob.ReqQueue(); public static void start(final int port) throws Exception { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); try { serverBootstrap.channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .group(boss, worker) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); /** * 不加不能直接访问 */ // SSLEngine sslEngine = SSLContextFactory.getSslContext().createSSLEngine(); // sslEngine.setUseClientMode(false); // pipeline.addLast("SslHandler", new SslHandler(sslEngine)); pipeline.addLast("HttpServerCodec", new HttpServerCodec()); pipeline.addLast("HttpObjectAggregator", new HttpObjectAggregator(Integer.MAX_VALUE)); pipeline.addLast("BodyToResponseEncoder", new HttpServerJob.BodyToResponseEncoder()); pipeline.addLast("RequestToBodyDecoder", new RequestToBodyDecoder()); } }); ChannelFuture future = serverBootstrap.bind(port).sync(); System.out.println("http server start at " + port); reqQueue.startConsumer(); future.channel().closeFuture().sync(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) throws Exception { start(1999); } private static class SSLContextFactory { public static SSLContext getSslContext() throws Exception { char[] passArray = "hsc123".toCharArray(); SSLContext sslContext = SSLContext.getInstance("TLSv1"); KeyStore ks = KeyStore.getInstance("JKS"); FileInputStream inputStream = new FileInputStream("C:\\Users\\xxx\\local.jks"); ks.load(inputStream, passArray); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, passArray); sslContext.init(kmf.getKeyManagers(), null, null); inputStream.close(); return sslContext; } } private static class RequestToBodyDecoder extends MessageToMessageDecoder<FullHttpRequest> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("active"); super.channelActive(ctx); } @Override protected void decode(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest, List<Object> list) throws Exception { if ("CONNECT".equalsIgnoreCase(fullHttpRequest.getMethod().name())) {//HTTPS建立代理握手 HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); channelHandlerContext.writeAndFlush(response); System.out.println("ssl request"); SSLEngine sslEngine = SSLContextFactory.getSslContext().createSSLEngine(); sslEngine.setUseClientMode(false); channelHandlerContext.pipeline().addFirst("SslHandler", new SslHandler(sslEngine)); return; } /** * 不能直接访问 */ // FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); // httpResponse.content().writeBytes("https".getBytes()); // httpResponse.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html;charset=UTF-8"); // httpResponse.headers().set(HttpHeaders.Names.CONTENT_LENGTH, httpResponse.content().readableBytes()); // channelHandlerContext.writeAndFlush(httpResponse); // if(true)return; HttpMethod httpMethod = fullHttpRequest.getMethod(); String uri = fullHttpRequest.getUri(); HttpHeaders httpHeaders = fullHttpRequest.headers(); Map<String, String> map = new HashMap<>(); for (Map.Entry<String, String> entry : httpHeaders.entries()) { map.put(entry.getKey(), entry.getValue()); } ByteBuf msg = fullHttpRequest.content(); byte[] bs = new byte[msg.readableBytes()]; msg.readBytes(bs); HttpServerJob.OriginHttp originHttp = new HttpServerJob.OriginHttp(httpMethod.name(), uri, map, bs, channelHandlerContext); String host = fullHttpRequest.headers().get("host"); String url = "https://" + host + "/"; String auth = "xxx"; originHttp.setUrl(url); originHttp.setAuth(auth); reqQueue.put(originHttp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); super.exceptionCaught(ctx, cause); } } }
可以看到 目标chrome的证书已经是localhost签发的
http=127.0.0.1:1999;https=127.0.0.1:1999
3.3 有很多js css无法通过代理加载
3.4
3.4.1 发现这个代理对公司的tomcat,只需要输入一次thisisunsafe就能100%跑出来,但在家里网络对百度、简书就不行,总有一些文件红了
3.4.2 实践中,发现每次输入另一个域名都要输入一次thisisunsafe,所以这个自签名证书并不是输入一次信任就行了
3.4.3 结合3.4.2判断是这个问题,找到这些红的文件,发现他们跟主域名(即输入过thisisunsafe的)都不同,这些文件在浏览器发出请求时都没有机会输,所以请求失败
3.4.4 公司网站因为都是内部js(/xxx/js)故没有暴露这个问题,公网网站乱七八糟的外部资源多
3.5 证书
keytool -genkey -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass hsc123 -storepass hsc123 -keystore local.jks
JoycedeMacBook:work joyce$ cd /Users/joyce/work/MyTest/HttpProxy/
JoycedeMacBook:HttpProxy joyce$ keytool -importkeystore -srckeystore local.jks -destkeystore local.jks -deststoretype pkcs12
keytool 错误: java.io.IOException: DerInputStream.getLength(): lengthTag=109, too big.
JoycedeMacBook:HttpProxy joyce$ ll
total 24
drwxr-xr-x 7 joyce staff 224 Aug 11 22:52 ./
drwxr-xr-x 23 joyce staff 736 Aug 11 22:46 ../
-rw-r--r-- 1 joyce staff 1196 Aug 11 22:48 HttpProxy.iml
-rw-r--r-- 1 joyce staff 2066 Aug 11 22:52 local.jks
-rw-r--r-- 1 joyce staff 2154 Aug 11 22:46 pom.xml
drwxr-xr-x 3 joyce staff 96 Aug 11 22:45 src/
drwxr-xr-x 4 joyce staff 128 Aug 11 22:52 target/
JoycedeMacBook:HttpProxy joyce$ keytool -importkeystore -srckeystore local.jks -destkeystore local0.jks -deststoretype pkcs12
输入目标密钥库口令:
再次输入新口令:
输入源密钥库口令:
已成功导入别名 mykey 的条目。
已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消
JoycedeMacBook:HttpProxy joyce$ ll
total 32
drwxr-xr-x 8 joyce staff 256 Aug 15 11:48 ./
drwxr-xr-x 23 joyce staff 736 Aug 11 22:46 ../
-rw-r--r-- 1 joyce staff 1196 Aug 11 22:48 HttpProxy.iml
-rw-r--r-- 1 joyce staff 2066 Aug 11 22:52 local.jks
-rw-r--r-- 1 joyce staff 2402 Aug 15 11:48 local0.jks
-rw-r--r-- 1 joyce staff 2154 Aug 11 22:46 pom.xml
drwxr-xr-x 3 joyce staff 96 Aug 11 22:45 src/
drwxr-xr-x 4 joyce staff 128 Aug 11 22:52 target/
JoycedeMacBook:HttpProxy joyce$ rm local.jks
JoycedeMacBook:HttpProxy joyce$ mv local0.jks local.jks
JoycedeMacBook:HttpProxy joyce$ keytool -list -rfc --keystore local.jks | openssl x509 -inform pem -pubkey
输入密钥库口令: hsc123
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkJIdaFvYYFa3lX3HJ49J
KBXSXjnyDwTba7Tt08bTnZUS8tszX6Id1uYMCvKCCcM4JZWeFac5k16wtq2uL6dN
Tz64ZSoQpWNo3/9vW9EBNjtc4fI0gDISkJEU7rDNh5QetMawuI8TBF0UZ7TxhtRj
LIDSHsOcGW8r6UHxpI85yz4/5haVy5WBB01e0f+CtkBP0RIn87fgXKs6THLXYPUv
tc6Q9HpEVza9UuS9JItjk88Ti0Sl6uu9uG8J8dddH+LYXX7rsp7Bz5gdNZTlDfMb
7OjlpIlY+6zGayz6kT9aFPJ8aKs1xtkRk77JHFMqUYGGE+1dUhVkXSJULxmRJnHC
2QIDAQAB
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
MIICxzCCAa+gAwIBAgIEPpwDEDANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls
b2NhbGhvc3QwHhcNMjIwODExMTQ1MjAyWhcNMjMwODExMTQ1MjAyWjAUMRIwEAYD
VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCQ
kh1oW9hgVreVfccnj0koFdJeOfIPBNtrtO3TxtOdlRLy2zNfoh3W5gwK8oIJwzgl
lZ4VpzmTXrC2ra4vp01PPrhlKhClY2jf/29b0QE2O1zh8jSAMhKQkRTusM2HlB60
xrC4jxMEXRRntPGG1GMsgNIew5wZbyvpQfGkjznLPj/mFpXLlYEHTV7R/4K2QE/R
Eifzt+BcqzpMctdg9S+1zpD0ekRXNr1S5L0ki2OTzxOLRKXq6724bwnx110f4thd
fuuynsHPmB01lOUN8xvs6OWkiVj7rMZrLPqRP1oU8nxoqzXG2RGTvskcUypRgYYT
7V1SFWRdIlQvGZEmccLZAgMBAAGjITAfMB0GA1UdDgQWBBQcyvPPDD289Q6lB+8M
75J+xa7hoDANBgkqhkiG9w0BAQsFAAOCAQEAAfPRZMh20IYziT09cWIxgKoN5h8X
RG3vNYiXyf8B8WPfRVNPX5EDFlqgKYPCbr09hJkbAI0ZDxTeImAaoSDvjg8ov33Y
EZuo0p0RNBB8wLTgrG2usuVuWu6t3jo1lmcaKg0T/ZZdtO4u7KnMcor6aUzfyzbW
qPiMj08L0B5PiJxVw/tqCWboFBqv1aSA3oJ7fnylsASQbEXwlbi2UY6MSj+ZmXzD
P/Mx/Im2K0/nyj4V27B6X80DPGFMkjkHueBUUSZYiMZnnM1LiMypLcuqQnCLnzzD
ME9/zCi8svp9pfivMOGxSG2gIyUfVKX87Kf4pMD14g9mZfeBNosW7/YDIQ==
-----END CERTIFICATE-----
JoycedeMacBook:HttpProxy joyce$ keytool -export -alias test -keystore local.jks -file local-publickey.cer
输入密钥库口令:
keytool 错误: java.lang.Exception: 别名 <test> 不存在
JoycedeMacBook:HttpProxy joyce$ keytool -export -localhost test -keystore local.jks -file local-publickey.cer
非法选项: -localhost
keytool -exportcert [OPTION]...
导出证书
选项:
-rfc 以 RFC 样式输出
-alias <alias> 要处理的条目的别名
-file <filename> 输出文件名
-keystore <keystore> 密钥库名称
-storepass <arg> 密钥库口令
-storetype <storetype> 密钥库类型
-providername <providername> 提供方名称
-providerclass <providerclass> 提供方类名
-providerarg <arg> 提供方参数
-providerpath <pathlist> 提供方类路径
-v 详细输出
-protected 通过受保护的机制的口令
使用 "keytool -help" 获取所有可用命令
JoycedeMacBook:HttpProxy joyce$ keytool -export -alias localhost -keystore local.jks -file local-publickey.cer
输入密钥库口令:
keytool 错误: java.lang.Exception: 别名 <localhost> 不存在
JoycedeMacBook:HttpProxy joyce$ keytool -export -keystore local.jks -file local-publickey.cer
输入密钥库口令:
存储在文件 <local-publickey.cer> 中的证书
safari 信任 chrom不信任
参考:
https://blog.csdn.net/xad707348125/article/details/47017851
https://www.freesion.com/article/48661311806/
4 连接泄漏
4.1 发现现象
4.1.1 家里网络mac
只能打开3-4个网页,之后便打不开了
4.1.2 公司网络win
maven密码加密,在maven使用代理时,发现过一段时间(当然比家里网络mac时间长的多)maven就阻塞了,不再发起请求
4.2 分析
猜测连接过度,浏览器虽然有6个连接限制,但代理服务器似乎不受控制,不同host不复用,最终代理服务器无法承载更多连接,也说明有连接泄漏
公司的maven代理执行时间长及其它内网网站未暴露这个问题,可能是因为maven及内网网站同host重复可用连接多,也可能是机器强,win连接能开的多
但是后来想一想并不完全是这样,maven代理有个特征每次都执行到某几个jar就阻塞了,而且重启服务器没用,这与家里重启服务就好了的现象不符合
4.3 验证
家里网络验证
netstat -an|grep 1999
发现连接逐渐变多,在比较少的时候,网页可以打开,比较多了之后,无法再打开,验证了猜想
4.4 修改
异常-连接泄漏 这个洞堵上后,正常(家里网络复核成功)
package com.jds.test.httpproxy.miniserver; /** * Created by joyce on 2022/8/11. */ import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.http.*; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicInteger; //https://www.cnblogs.com/silyvin/articles/16573161.html public class HttpServerJob implements Runnable { public static AtomicInteger open = new AtomicInteger(0); public static AtomicInteger close = new AtomicInteger(0); final EventLoopGroup CLIENT_BOSS_LOOP_GROUP = new NioEventLoopGroup(1); final EventLoopGroup CLIENT_WORKER_LOOP_GROUP = new NioEventLoopGroup(4); public static void main(String[] args) throws Exception { new Thread(new HttpServerJob(8990, "https://xxx/", "xxx")).start(); new Thread(new HttpServerJob(8990, "https://xxx/", "xxx")).start(); new Thread(new HttpServerJob(8990, "https://xxx/", "xxx")).start(); } public HttpServerJob(int port, String url, String auth) { this.port = port; this.url = url; this.auth = auth; } private int port; private String url; private ReqQueue reqQueue = new ReqQueue(); private String auth; @Override public void run() { startHttpServer(); } public void startHttpServer() { try { ServerBootstrap bs = new ServerBootstrap(); bs.group(CLIENT_BOSS_LOOP_GROUP, CLIENT_WORKER_LOOP_GROUP); bs.channel(NioServerSocketChannel.class); bs.childHandler(new HttpServerInitializer()); ChannelFuture future = bs.bind(this.port).sync(); System.out.println("http server start at " + port); reqQueue.startConsumer(); future.channel().closeFuture().sync(); } catch (Exception e) { e.printStackTrace(); } finally { CLIENT_BOSS_LOOP_GROUP.shutdownGracefully(); CLIENT_WORKER_LOOP_GROUP.shutdownGracefully(); } } private class HttpServerInitializer extends ChannelInitializer<SocketChannel> { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new HttpObjectAggregator(Integer.MAX_VALUE)); pipeline.addLast(new BodyToResponseEncoder()); pipeline.addLast(new RequestToBodyDecoder()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); super.exceptionCaught(ctx, cause); } } public static class ReqQueue { private BlockingQueue<OriginHttp> arrayBlockingQueue = new LinkedBlockingDeque<OriginHttp>(100); public void startConsumer() { for(int i=0; i<10; ++i) { new Thread(new Customer()).start(); } } public void put(OriginHttp originHttp) { try { arrayBlockingQueue.put(originHttp); } catch (InterruptedException e) { e.printStackTrace(); } } private class Customer implements Runnable { @Override public void run() { try { while (true){ OriginHttp originHttp = arrayBlockingQueue.take(); ProxySender.send(originHttp, originHttp.getUrl()); Thread.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } } } } private class RequestToBodyDecoder extends MessageToMessageDecoder<FullHttpRequest> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("active " + open.incrementAndGet()); super.channelActive(ctx); } @Override protected void decode(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest, List<Object> list) throws Exception { HttpMethod httpMethod = fullHttpRequest.getMethod(); String uri = fullHttpRequest.getUri(); HttpHeaders httpHeaders = fullHttpRequest.headers(); Map<String, String> map = new HashMap<>(); for (Map.Entry<String, String> entry : httpHeaders.entries()) { map.put(entry.getKey(), entry.getValue()); } ByteBuf msg = fullHttpRequest.content(); byte[] bs = new byte[msg.readableBytes()]; msg.readBytes(bs); OriginHttp originHttp = new OriginHttp(httpMethod.name(), uri, map, bs, channelHandlerContext); originHttp.setUrl(url); originHttp.setAuth(auth); reqQueue.put(originHttp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.channel().close(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("connect release " + close.incrementAndGet()); super.channelInactive(ctx); } } public static class BodyToResponseEncoder extends MessageToMessageEncoder<ResHttp> { @Override protected void encode(ChannelHandlerContext channelHandlerContext, ResHttp resHttp, List<Object> list) throws Exception { FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(resHttp.getRet()), Unpooled.wrappedBuffer(resHttp.getBody())); Map<String, String> headers = resHttp.getHeaders(); for(Map.Entry<String, String> entry : headers.entrySet()) { response.headers().set(entry.getKey(), entry.getValue()); if(entry.getKey().equals("Set-Cookie")) { response.headers().set(entry.getKey(), entry.getValue().replace("Secure", "").replace("HttpOnly", "")); } } response.headers().remove("X-Frame-Options"); response.headers().set("testproxy", "hhh"); /** * one of content_length and chunked in response header neccesary in http long connections */ if(!response.headers().contains("Transfer-Encoding") && !"chunked".equals(response.headers().get("Transfer-Encoding"))) response.headers().set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes()); // response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); // response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, "*"); // response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_HEADERS, HttpHeaders.Names.CONTENT_TYPE); list.add(response); } } public static class OriginHttp { private String url; private String method; private String uri; private Map<String, String> headers; private byte [] body; ChannelHandlerContext context; private String auth; public OriginHttp(String method, String uri, Map<String, String> headers, byte [] body, ChannelHandlerContext context) { this.method = method; this.uri = uri; this.headers = headers; this.body = body; this.context = context; } public String getMethod() { return method; } public String getUri() { return uri; } public Map<String, String> getHeaders() { return headers; } public byte[] getBody() { return body; } public ChannelHandlerContext getContext() { return context; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getAuth() { return auth; } public void setAuth(String auth) { this.auth = auth; } } private static class ProxySender { public static void send(OriginHttp originHttp, String dir) { try { String method = originHttp.getMethod(); String uri = originHttp.getUri(); String url = new StringBuilder(dir).append(uri).toString(); System.out.println(url); HttpUriRequest httpUriRequest = null; if ("GET".equals(method)) { httpUriRequest = new HttpGet(url); } else if ("POST".equals(method)) { httpUriRequest = new HttpPost(url); HttpPost httpPost = (HttpPost) httpUriRequest; try { String st = new String(originHttp.getBody()); httpPost.setEntity(new StringEntity(st)); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } Map<String, String> headers = originHttp.getHeaders(); for (Map.Entry<String, String> entry : headers.entrySet()) { String val = entry.getValue(); httpUriRequest.addHeader(entry.getKey(), val); } // httpUriRequest.addHeader("Cookie", cookie); httpUriRequest.removeHeaders("Content-Length"); httpUriRequest.removeHeaders("Origin"); httpUriRequest.removeHeaders("Referer"); httpUriRequest.removeHeaders("Host"); httpUriRequest.addHeader("Origin", dir); httpUriRequest.addHeader("Referer", dir); httpUriRequest.addHeader("sm_user", originHttp.getAuth()); // httpUriRequest.addHeader("Host", dir); CloseableHttpClient httpClient = HttpClientFactory.createSSLClientDefault(); org.apache.http.HttpResponse response = null; response = httpClient.execute(httpUriRequest); Map<String, String> map = new HashMap<>(); Header[] headersRes = response.getAllHeaders(); for (Header header : headersRes) { map.put(header.getName(), header.getValue()); } int ret = response.getStatusLine().getStatusCode(); HttpEntity responseEntity = response.getEntity(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); if(responseEntity != null) { InputStream inputStream = responseEntity.getContent(); byte[] bytes = new byte[1024]; int i = 0; while ((i = inputStream.read(bytes)) != -1) { byteArrayOutputStream.write(bytes, 0, i); } } ResHttp resHttp = new ResHttp(ret, map, byteArrayOutputStream.toByteArray()); ChannelFuture channelFuture = originHttp.getContext().writeAndFlush(resHttp); channelFuture.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { /** * to use http short connections by this line */ future.channel().close(); if (!future.isSuccess()) { future.cause().printStackTrace(); //future.channel().close(); } } }); } catch (Exception e) { e.printStackTrace(); originHttp.context.channel().close(); } finally { } } } private static class HttpClientFactory { public static CloseableHttpClient createSSLClientDefault() { try { SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException { return true; } }).build(); HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE; SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier); return HttpClients.custom().setSSLSocketFactory(sslsf).build(); } catch (Exception e) { e.printStackTrace(); } return HttpClients.createDefault(); } } private static class ResHttp { private int ret; private Map<String, String> headers; private byte [] body; public ResHttp(int ret, Map headers, byte [] body) { this.ret = ret; this.headers = headers; this.body = body; } public int getRet() { return ret; } public Map<String, String> getHeaders() { return headers; } public byte[] getBody() { return body; } } }
close为啥不放finally?因为write是异步的
package com.jds.test.httpproxy.miniserver; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.ssl.SslHandler; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import java.io.FileInputStream; import java.security.KeyStore; import java.security.KeyStoreException; import java.util.HashMap; import java.util.List; import java.util.Map; import io.netty.handler.codec.http.*; /** * Created by joyce on 2022/8/11. */ public class ProxyHttpsDecode { private static HttpServerJob.ReqQueue reqQueue = new HttpServerJob.ReqQueue(); public static void start(final int port) throws Exception { EventLoopGroup boss = new NioEventLoopGroup(); EventLoopGroup worker = new NioEventLoopGroup(); ServerBootstrap serverBootstrap = new ServerBootstrap(); try { serverBootstrap.channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .group(boss, worker) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); /** * 不加不能直接访问 */ // SSLEngine sslEngine = SSLContextFactory.getSslContext().createSSLEngine(); // sslEngine.setUseClientMode(false); // pipeline.addLast("SslHandler", new SslHandler(sslEngine)); pipeline.addLast("HttpServerCodec", new HttpServerCodec()); pipeline.addLast("HttpObjectAggregator", new HttpObjectAggregator(Integer.MAX_VALUE)); pipeline.addLast("BodyToResponseEncoder", new HttpServerJob.BodyToResponseEncoder()); pipeline.addLast("RequestToBodyDecoder", new RequestToBodyDecoder()); } }); ChannelFuture future = serverBootstrap.bind(port).sync(); System.out.println("http server start at " + port); reqQueue.startConsumer(); future.channel().closeFuture().sync(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) throws Exception { start(1999); } private static class SSLContextFactory { public static SSLContext getSslContext() throws Exception { char[] passArray = "hsc123".toCharArray(); SSLContext sslContext = SSLContext.getInstance("TLSv1"); KeyStore ks = KeyStore.getInstance("JKS"); FileInputStream inputStream = new FileInputStream("/Users/joyce/work/MyTest/HttpProxy/local.jks"); ks.load(inputStream, passArray); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, passArray); sslContext.init(kmf.getKeyManagers(), null, null); inputStream.close(); return sslContext; } } private static class RequestToBodyDecoder extends MessageToMessageDecoder<FullHttpRequest> { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("active " + HttpServerJob.open.incrementAndGet()); super.channelActive(ctx); } @Override protected void decode(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest, List<Object> list) throws Exception { if ("CONNECT".equalsIgnoreCase(fullHttpRequest.getMethod().name())) {//HTTPS建立代理握手 HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); channelHandlerContext.writeAndFlush(response); System.out.println("ssl request"); SSLEngine sslEngine = SSLContextFactory.getSslContext().createSSLEngine(); sslEngine.setUseClientMode(false); channelHandlerContext.pipeline().addFirst("SslHandler", new SslHandler(sslEngine)); return; } /** * 不能直接访问 */ // FullHttpResponse httpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); // httpResponse.content().writeBytes("https".getBytes()); // httpResponse.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/html;charset=UTF-8"); // httpResponse.headers().set(HttpHeaders.Names.CONTENT_LENGTH, httpResponse.content().readableBytes()); // channelHandlerContext.writeAndFlush(httpResponse); // if(true)return; HttpMethod httpMethod = fullHttpRequest.getMethod(); String uri = fullHttpRequest.getUri(); HttpHeaders httpHeaders = fullHttpRequest.headers(); Map<String, String> map = new HashMap<>(); for (Map.Entry<String, String> entry : httpHeaders.entries()) { map.put(entry.getKey(), entry.getValue()); } ByteBuf msg = fullHttpRequest.content(); byte[] bs = new byte[msg.readableBytes()]; msg.readBytes(bs); HttpServerJob.OriginHttp originHttp = new HttpServerJob.OriginHttp(httpMethod.name(), uri, map, bs, channelHandlerContext); String host = fullHttpRequest.headers().get("host"); String url = "https://" + host + "/"; String auth = "xxx"; originHttp.setUrl(url); originHttp.setAuth(auth); reqQueue.put(originHttp); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.channel().close(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("connect release " + HttpServerJob.close.incrementAndGet()); super.channelInactive(ctx); } } }
5 应用
5.1 iframe
ifram三大困难:
跨域允许iframe,cookie,自签名ssl
第一个:
response.headers().remove("X-Frame-Options");
第二个:
用这种方式本质是建立一个同host不同端口的中间人,注意同host不同端口cookie是共享的 netty(二十三)mycas framework
第三个:
代理本身与母网站是同host,无论是CA还是自签名,浏览器都会放
5.2 demo 录像数据脱敏
5.3 鉴权
server跑maven但不允许出现密码,那让server转发到个人机器,中间加上鉴权
5.4 跨域ajax
是否允许跨域
response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
cookie,同iframe 及 with-creditial
自签名ssl,同iframe