一、导入netty 包 和证书相关依赖(gradle)
//证书创建的依赖包
api("org.bouncycastle:bcpkix-jdk15on:1.60")
//netty 依赖包
implementation 'io.netty:netty-all:4.1.99.Final'
二、证书创建工具类(https)
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.internal.StringUtil;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* https支持工具类
*
*/
public class HttpsSupport {
/**
* 证书
*/
private SslContext clientSslCtx;
/**
* 证书使用者
*/
private String issuer;
/**
* 证书开始时间
*/
private Date caNotBefore;
/**
* 证书结束时间
*/
private Date caNotAfter;
/**
* ca私钥
*/
private PrivateKey caPriKey;
/**
* 服务端私钥
*/
private PrivateKey serverPriKey;
/**
* 服务端公钥
*/
private PublicKey serverPubKey;
/**
* 证书cahce
*/
private Map<String, X509Certificate> certCache = new HashMap<>();
/**
*
*/
private KeyFactory keyFactory = null;
private HttpsSupport() {
initHttpsConfig();
}
private static HttpsSupport httpsSupport;
public static HttpsSupport getInstance() {
if (httpsSupport == null) {
httpsSupport = new HttpsSupport();
}
return httpsSupport;
}
private void initHttpsConfig() {
try {
keyFactory = KeyFactory.getInstance("RSA");
// keyFactory = KeyFactory.getInstance("ECC");
//信任客户端的所有证书,不进行校验
setClientSslCtx(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build());
//加载证书
// ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//从项目目录加入ca根证书
// X509Certificate caCert = loadCert(classLoader.getResourceAsStream("ca.crt"));
X509Certificate caCert = loadCert( AssertsFileUtils.readFileFromAssetsInputStream(null,"httpCert/ca.crt"));
// X509Certificate caCert = loadCert( AssertsFileUtils.readFileFromAssetsInputStream(null,"httpCert/oneid.crt"));
//从项目目录加入ca私钥
// PrivateKey caPriKey = loadPriKey(classLoader.getResourceAsStream("ca_private.der"));
PrivateKey caPriKey = loadPriKey(AssertsFileUtils.readFileFromAssetsInputStream(null,"httpCert/ca_private.der"));
// PrivateKey caPriKey = loadPriKeyFanse(AssertsFileUtils.readFileFromAssetsInputStream(null,"httpCert/oneid_ca.key"));
setCaPriKey(caPriKey);
//从证书中获取使用者信息
setIssuer(getSubjectByCert(caCert));
//设置ca证书有效期
setCaNotBefore(caCert.getNotBefore());
setCaNotAfter(caCert.getNotAfter());
//生产一对随机公私钥用于网站SSL证书动态创建
KeyPair keyPair = genKeyPair();
//server端私钥
setServerPriKey(keyPair.getPrivate());
//server端公钥
setServerPubKey(keyPair.getPublic());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成RSA公私密钥对,长度为2048
*/
private KeyPair genKeyPair() throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// KeyPairGenerator caKeyPairGen = KeyPairGenerator.getInstance("RSA", "BC");
KeyPairGenerator caKeyPairGen = KeyPairGenerator.getInstance("RSA");
caKeyPairGen.initialize(2048, new SecureRandom());
return caKeyPairGen.genKeyPair();
}
/**
* 获取证书中的subject信息
*/
private String getSubjectByCert(X509Certificate certificate) {
//读出来顺序是反的需要反转下
List<String> tempList = Arrays.asList(certificate.getIssuerDN().toString().split(", "));
return IntStream.rangeClosed(0, tempList.size() - 1)
.mapToObj(i -> tempList.get(tempList.size() - i - 1)).collect(Collectors.joining(", "));
}
/**
* 加载ca的私钥
*
* @param inputStream ca私钥文件流
*/
private PrivateKey loadPriKey(InputStream inputStream) throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bts = new byte[1024];
int len;
while ((len = inputStream.read(bts)) != -1) {
outputStream.write(bts, 0, len);
}
inputStream.close();
outputStream.close();
return loadPriKey(outputStream.toByteArray());
}
/**
* 加载ca的私钥
*
* @param inputStream ca私钥文件流
*/
private PrivateKey loadPriKeyFanse(InputStream inputStream) throws Exception {
Class<?>[] parameterTypes = new Class[]{InputStream.class,String.class};
Method method = SslContext.class.getDeclaredMethod("toPrivateKey", parameterTypes);
// 设置访问权限为true,表示能够访问到被保护的方法
method.setAccessible(true);
return (PrivateKey) method.invoke(null,inputStream,null);
}
/**
* 从文件加载RSA私钥 openssl pkcs8 -topk8 -nocrypt -inform PEM -outform DER -in ca.key -out
* ca_private.der
*/
private PrivateKey loadPriKey(byte[] bts)
throws Exception {
EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(bts);
return keyFactory.generatePrivate(privateKeySpec);
}
/**
* 加载ca根证书
*
* @param inputStream 证书文件流
*/
private X509Certificate loadCert(InputStream inputStream) throws Exception {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(inputStream);
}
public SslContext getClientSslCtx() {
return clientSslCtx;
}
public void setClientSslCtx(SslContext clientSslCtx) {
this.clientSslCtx = clientSslCtx;
}
public String getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = issuer;
}
public Date getCaNotBefore() {
return caNotBefore;
}
public void setCaNotBefore(Date caNotBefore) {
this.caNotBefore = caNotBefore;
}
public Date getCaNotAfter() {
return caNotAfter;
}
public void setCaNotAfter(Date caNotAfter) {
this.caNotAfter = caNotAfter;
}
public PrivateKey getCaPriKey() {
return caPriKey;
}
public void setCaPriKey(PrivateKey caPriKey) {
this.caPriKey = caPriKey;
}
public PrivateKey getServerPriKey() {
return serverPriKey;
}
public void setServerPriKey(PrivateKey serverPriKey) {
this.serverPriKey = serverPriKey;
}
public PublicKey getServerPubKey() {
return serverPubKey;
}
public void setServerPubKey(PublicKey serverPubKey) {
this.serverPubKey = serverPubKey;
}
/**
* 获取证书
*
* @param host host
* @return host对应的证书
*/
public X509Certificate getCert(String host) throws Exception {
// host = "localHost";
if (StringUtil.isNullOrEmpty(host)) {
return null;
}
X509Certificate cacheCert = certCache.get(host);
if (cacheCert != null) {
//将缓存的证书返回
return cacheCert;
}
//生成新的证书,并将它放到缓存中去
host = host.trim().toLowerCase();
String hostLowerCase = host.trim().toLowerCase();
X509Certificate cert = genCert(getIssuer(), getCaPriKey(), getCaNotBefore(), getCaNotAfter(), getServerPubKey(), hostLowerCase);
//添加到缓存
certCache.put(host, cert);
return certCache.get(host);
}
/**
* 动态生成服务器证书,并进行CA签授
*
* @param issuer 颁发机构
*/
/**
* @param issuer 颁发机构
* @param caPriKey ca私钥
* @param certStartTime 证书开始时间
* @param certEndTime 证书结束时间
* @param serverPubKey server证书的公钥
* @param hosts host,支持同时生成多个host
* @return 证书
* @throws Exception Exception
*/
public static X509Certificate genCert(String issuer, PrivateKey caPriKey, Date certStartTime,
Date certEndTime, PublicKey serverPubKey,
String... hosts) throws Exception {
//根据CA证书subject来动态生成目标服务器证书的issuer和subject
String subject = "C=CN, ST=SC, L=CD, O=hai, OU=study, CN=" + hosts[0];
JcaX509v3CertificateBuilder jv3Builder = new JcaX509v3CertificateBuilder(new X500Name(issuer),
//序列号,需要唯一;ElementaryOS上证书不安全问题(serialNumber为1时证书会提示不安全),避免serialNumber冲突,采用时间戳+4位随机数生成
BigInteger.valueOf(System.currentTimeMillis() + (long) (Math.random() * 10000) + 1000),
certStartTime,
certEndTime,
new X500Name(subject),
serverPubKey);
//SAN扩展证书支持的域名,否则浏览器提示证书不安全
GeneralName[] generalNames = new GeneralName[hosts.length];
for (int i = 0; i < hosts.length; i++) {
generalNames[i] = new GeneralName(GeneralName.dNSName, hosts[i]);
}
GeneralNames subjectAltName = new GeneralNames(generalNames);
//添加多域名支持
jv3Builder.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
//SHA256 用SHA1浏览器可能会提示证书不安全
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPriKey);
return new JcaX509CertificateConverter().getCertificate(jv3Builder.build(signer));
}
}
三、代理请求工具类
import com.jingantech.httpproxydemo.bean.ClientRequest;
import com.jingantech.httpproxydemo.bean.Constans;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.Attribute;
import static com.jingantech.httpproxydemo.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY;
/**
* 代理请求工具类
*
*/
public class ProxyRequestUtil {
private ProxyRequestUtil() {
}
/**
* 获取代理请求
*
* @param httpRequest http请求
*/
public static ClientRequest getClientReuqest(HttpRequest httpRequest) {
//从header中获取出host
String host = httpRequest.headers().get("host");
//从host中获取出端口
String[] hostStrArr = host.split(":");
int port = 80;
if (hostStrArr.length > 1) {
port = Integer.parseInt(hostStrArr[1]);
} else if (httpRequest.uri().startsWith(Constans.HTTPS_PROTOCOL_NAME)) {
port = 443;
}
return new ClientRequest(hostStrArr[0], port);
}
/**
* 从channel中获取clientRequest
*/
public static ClientRequest getClientRequest(Channel channel) {
//将clientRequest保存到channel中
Attribute<ClientRequest> clientRequestAttribute = channel.attr(CLIENTREQUEST_ATTRIBUTE_KEY);
return clientRequestAttribute.get();
}
}
四、读取asset目录下证书的工具类
import android.content.Context;
import android.content.res.AssetManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
/**
* asserts文件处理
*/
public class AssertsFileUtils {
private AssertsFileUtils() {
}
private static Context mContext;
public static Context getContext() {
return mContext;
}
public static void setContext(Context context) {
AssertsFileUtils.mContext = context;
}
/**
* 读取asserts目录下的文件
*
* @param fileName eg:"updatelog.txt"
* @return 对应文件的内容
*/
public static String readFileFromAssets(Context context, String fileName) throws IOException, IllegalArgumentException {
if(context==null){
context = mContext;
}
AssetManager assetManager = context.getAssets();
InputStream input = assetManager.open(fileName);
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = 0;
while ((length = input.read(buffer)) != -1) {
output.write(buffer, 0, length);
}
output.close();
input.close();
return output.toString();
}
public static InputStream readFileFromAssetsInputStream(Context context, String fileName) throws Exception {
if(context==null){
context = mContext;
}
AssetManager assetManager = context.getAssets();
InputStream input = assetManager.open(fileName);
return input;
}
/**
* 列出Asserts文件夹下的所有文件
*
* @return asserts目录下的文件名列表
*/
public static List<String> getAssertsFiles(Context context) throws IllegalArgumentException {
if (null == context) {
throw new IllegalArgumentException("bad arguments!");
}
AssetManager assetManager = context.getAssets();
String[] files = null;
try {
files = assetManager.list("");
} catch (IOException e) {
e.printStackTrace();
}
return (null == files) ? null : Arrays.asList(files);
}
}
五、代理hander (处理代理请求的数据 request 和 response)
1、httpHander
import static com.jingantech.httpproxydemo.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY;
import android.util.Log;
import com.jingantech.httpproxydemo.bean.ClientRequest;
import com.jingantech.httpproxydemo.bean.Constans;
import com.jingantech.httpproxydemo.handler.response.HttpProxyResponseHandler;
import com.jingantech.httpproxydemo.utils.ProxyRequestUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.Attribute;
import io.netty.util.ReferenceCountUtil;
/**
* 对http请求进行代理
*/
public class HttpProxyHandler extends ChannelInboundHandlerAdapter implements IProxyHandler {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Log.i("HttpProxyHandler", "[HttpProxyHandler]");
if (msg instanceof HttpRequest) {
HttpRequest httpRequest = (HttpRequest) msg;
//可以篡改客户端请求的header
httpRequest.headers().set("DEVICE-ID", "01123123123132");
httpRequest.headers().set("x-forwarded-for", "10.10.1.181");
httpRequest.headers().set("x-forwarded-port", 443);
httpRequest.headers().set("Server", "MobileAndroidSdp");
//获取客户端请求
ClientRequest clientRequest = ProxyRequestUtil.getClientRequest(ctx.channel());
if (clientRequest == null) {
//从本次请求中获取
Attribute<ClientRequest> clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY);
clientRequest = ProxyRequestUtil.getClientReuqest(httpRequest);
//将clientRequest保存到channel中
clientRequestAttribute.setIfAbsent(clientRequest);
}
//如果是connect代理请求,返回成功以代表代理成功
if (sendSuccessResponseIfConnectMethod(ctx, httpRequest.method().name())) {
Log.i("HttpProxyHandler", "[HttpProxyHandler][channelRead] sendSuccessResponseConnect");
ctx.channel().pipeline().remove("httpRequestDecoder");
ctx.channel().pipeline().remove("httpResponseEncoder");
ctx.channel().pipeline().remove("httpAggregator");
ReferenceCountUtil.release(msg);
return;
}
if (clientRequest.isHttps()) {
//https请求不在此处转发
super.channelRead(ctx, msg);
return;
}
sendToServer(clientRequest, ctx, msg);
return;
}
super.channelRead(ctx, msg);
}
/**
* 如果是connect请求的话,返回连接建立成功
*
* @param ctx ChannelHandlerContext
* @param methodName 请求类型名
* @return 是否为connect请求
*/
private boolean sendSuccessResponseIfConnectMethod(ChannelHandlerContext ctx, String methodName) {
if (Constans.CONNECT_METHOD_NAME.equalsIgnoreCase(methodName)) {
//代理建立成功
//HTTP代理建立连接
HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, Constans.CONNECT_SUCCESS);
ctx.writeAndFlush(response);
return true;
}
return false;
}
@Override
public void sendToServer(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(ctx.channel().eventLoop())
// 注册线程池
.channel(ctx.channel().getClass())
// 使用NioSocketChannel来作为连接用的channel类
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
//添加接收远程server的handler
ch.pipeline().addLast(new HttpRequestEncoder());
ch.pipeline().addLast(new HttpResponseDecoder());
ch.pipeline().addLast(new HttpObjectAggregator(6553600));
//代理handler,负责给客户端响应结果
ch.pipeline().addLast(new HttpProxyResponseHandler(ctx.channel()));
}
});
//连接远程server
ChannelFuture cf = bootstrap.connect(clientRequest.getHost(), clientRequest.getPort());
cf.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
//AggregatedFullHttpRequest
//连接成功
future.channel().writeAndFlush(msg);
// logger.debug("[operationComplete] connect remote server success!");
Log.i("HttpProxyHandler", "[operationComplete] connect remote server success!");
} else {
//连接失败
// logger.error("[operationComplete] 连接远程server失败了");
Log.e("HttpProxyHandler", "[operationComplete] 连接远程server失败了");
ctx.channel().close();
}
}
});
}
@Override
public void sendToClient(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) {
}
}
2、httpsHandler
import static com.jingantech.httpproxydemo.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY;
import android.text.TextUtils;
import android.util.Log;
import com.jingantech.httpproxydemo.bean.ClientRequest;
import com.jingantech.httpproxydemo.handler.response.HttpProxyResponseHandler;
import com.jingantech.httpproxydemo.utils.HttpsSupport;
import java.util.logging.Logger;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.util.Attribute;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
/**
* 对https请求进行代理
*/
public class HttpsProxyHandler extends ChannelInboundHandlerAdapter implements IProxyHandler {
// private Logger logger = LoggerFactory.getLogger(HttpsProxyHandler.class);
private ChannelFuture httpsRequestCf;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// logger.debug("[HttpsProxyHandler]");
Attribute<ClientRequest> clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY);
ClientRequest clientRequest = clientRequestAttribute.get();
if (msg instanceof HttpRequest) {
HttpRequest httpRequest = (HttpRequest) msg;
clientRequest.setUri(httpRequest.uri());
if (httpRequest.method()==HttpMethod.POST){
clientRequest.setHttpRequest(httpRequest);
return;
}
sendToServer(clientRequest, ctx, msg);
} else if (msg instanceof HttpContent) {
if (msg instanceof DefaultLastHttpContent){
DefaultLastHttpContent defaultLastHttpContent = (DefaultLastHttpContent) msg;
clientRequest.setDefaultLastHttpContent(defaultLastHttpContent);
Log.d("HttpsProxyHandler","[HttpsProxyHandler][HttpContent]body!"+ ((HttpContent) msg).content().toString(CharsetUtil.UTF_8));
sendToServer(clientRequest,ctx,defaultLastHttpContent);
}else if (TextUtils.equals(msg.toString() ,"EmptyLastHttpContent")){
ReferenceCountUtil.release(msg);
}
Log.d("HttpsProxyHandler","[HttpsProxyHandler][HttpContent]不作处理!"+msg);
//content不做处理
// ReferenceCountUtil.release(msg);
} else {
ByteBuf byteBuf = (ByteBuf) msg;
// ssl握手
if (byteBuf.getByte(0) == 22) {
// logger.debug("[HttpsProxyHandler][do hands]");
Log.d("HttpsProxyHandler","[HttpsProxyHandler][do hands]");
sendToClient(clientRequest, ctx, msg);
}
}
}
@Override
public void sendToServer(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) {
Log.d("HttpsProxyHandler","[HttpsProxyHandler][sendToServer] 发送https请求到server");
Channel clientChannel = ctx.channel();
// if (httpsRequestCf!=null && httpsRequestCf.isSuccess()){
// httpsRequestCf.channel().writeAndFlush(msg);
// return;
// }
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(new NioEventLoopGroup(1))
// 注册线程池
.channel(NioSocketChannel.class)
// 使用NioSocketChannel来作为连接用的channel类
.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
//添加一个ssl处理器进行处理
ch.pipeline().addLast(
HttpsSupport.getInstance().getClientSslCtx().newHandler(ch.alloc(),
clientRequest.getHost(), clientRequest.getPort()));
ch.pipeline().addLast("httpCodec", new HttpClientCodec());
//添加响应处理器
ch.pipeline().addLast("proxyClientHandle", new HttpProxyResponseHandler(clientChannel));
}
});
httpsRequestCf = bootstrap.connect(clientRequest.getHost(), clientRequest.getPort());
//建立连接
httpsRequestCf.addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
if (clientRequest.getHttpRequest()!=null){
DefaultFullHttpRequest defaultFullHttpRequest = new DefaultFullHttpRequest(clientRequest.getHttpRequest().protocolVersion(),clientRequest.getHttpRequest().method(),clientRequest.getHttpRequest().uri(),clientRequest.getDefaultLastHttpContent().content(),clientRequest.getHttpRequest().headers(),HttpHeaders.EMPTY_HEADERS);
defaultFullHttpRequest.retain();
future.channel().writeAndFlush(defaultFullHttpRequest);
return;
}
Log.i("HttpsProxyHandler","发送"+clientRequest.getUri());
future.channel().writeAndFlush(msg);
} else {
Log.e("HttpsProxyHandler","[HttpsProxyHandler][sendToServer]连接远程server失败");
}
});
}
@Override
public void sendToClient(ClientRequest clientRequest, ChannelHandlerContext ctx, Object msg) {
try {
Log.d("HttpsProxyHandler","[HttpsProxyHandler][sendToClient] 与客户端进行https握手");
SslContext sslCtx = SslContextBuilder
.forServer(HttpsSupport.getInstance().getServerPriKey(), HttpsSupport.getInstance().getCert(clientRequest.getHost())).build();
//接收客户端请求,将客户端的请求内容解码
ctx.pipeline().addFirst("httpRequestDecoder", new HttpRequestDecoder());
//发送响应给客户端,并将发送内容编码
ctx.pipeline().addFirst("httpResponseEncoder", new HttpResponseEncoder());
//http聚合
ctx.pipeline().addLast("httpAggregator", new HttpObjectAggregator(65536));
//ssl处理
ctx.pipeline().addFirst("sslHandle", sslCtx.newHandler(ctx.alloc()));
// 重新过一遍pipeline,拿到解密后的的http报文
ctx.pipeline().fireChannelRead(msg);
Attribute<ClientRequest> clientRequestAttribute = ctx.channel().attr(CLIENTREQUEST_ATTRIBUTE_KEY);
clientRequest.setHttps(true);
clientRequestAttribute.set(clientRequest);
} catch (Exception e) {
Log.e("HttpsProxyHandler","[sendToServer] err:"+ e.getMessage());
}
}
}
3、handler 接口
import com.jingantech.httpproxydemo.bean.ClientRequest;
import io.netty.channel.ChannelHandlerContext;
/**
* 代理handler
*
*/
public interface IProxyHandler {
/**
* 发送到server
*
* @param clientRequest 客户端请求
* @param ctx ChannelHandlerContext
* @param msg 数据
*/
void sendToServer(ClientRequest clientRequest, final ChannelHandlerContext ctx, final Object msg);
/**
* 发送到client
*/
void sendToClient(ClientRequest clientRequest, final ChannelHandlerContext ctx, final Object msg);
}
4、responseHandler(服务端相应的数据拦截或者处理)
import static com.jingantech.httpproxydemo.bean.Constans.CLIENTREQUEST_ATTRIBUTE_KEY;
import android.text.TextUtils;
import android.util.Log;
import com.jingantech.httpproxydemo.bean.ClientRequest;
import java.nio.charset.Charset;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultHttpContent;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.util.Attribute;
import io.netty.util.ReferenceCountUtil;
/**
* https代理responseHandler
* created on 2019/10/28 15:00
*
* @author Karision.Gou
*/
public class HttpProxyResponseHandler extends ChannelInboundHandlerAdapter {
private Channel clientChannel;
public HttpProxyResponseHandler(Channel clientChannel) {
this.clientChannel = clientChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Attribute<ClientRequest> clientRequestAttribute = clientChannel.attr(CLIENTREQUEST_ATTRIBUTE_KEY);
if (msg instanceof FullHttpResponse) {
FullHttpResponse response = (FullHttpResponse) msg;
Log.d("HttpProxyResponseHandler", "[channelRead][FullHttpResponse] 接收到远程的数据1"+clientRequestAttribute.get().getUri()+" content:\n" + response.content().toString(Charset.defaultCharset()));
} else if (msg instanceof DefaultHttpResponse) {
DefaultHttpResponse response = (DefaultHttpResponse) msg;
Log.d("HttpProxyResponseHandler", "[channelRead][FullHttpResponse] 接收到远程的数据2 "+clientRequestAttribute.get().getUri()+"content:\n" + response);
} else if (msg instanceof DefaultHttpContent) {
DefaultHttpContent httpContent = (DefaultHttpContent) msg;
Log.d("HttpProxyResponseHandler", "[channelRead][DefaultHttpContent] "+clientRequestAttribute.get().getUri()+"\n接收到远程的数据3+"+clientRequestAttribute.get().getHost()+"content:{}" + httpContent.content().toString(Charset.defaultCharset()));
} else {
Log.d("HttpProxyResponseHandler", "[channelRead] 接收到远程的数据 \n" + msg.toString());
// if (TextUtils.equals(msg.toString(),"EmptyLastHttpContent")){
// ReferenceCountUtil.release(msg);
// }
}
//发送给客户端
clientChannel.writeAndFlush(msg);
}
}
六、创建代理服务类 ProxyServer
import android.util.Log;
import com.jingantech.httpproxydemo.handler.proxy.HttpProxyHandler;
import com.jingantech.httpproxydemo.handler.proxy.HttpsProxyHandler;
import com.jingantech.httpproxydemo.handler.proxy.SocksProxyHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.HttpPostRequestEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
/**
*/
public class ProxyServer {
private static ProxyServer instace = new EasyHttpProxyServer();
public static ProxyServer getInstace() {
if (instace == null) {
instace = new ProxyServer();
}
return instace;
}
/**
* 启动
*
* @param listenPort 监控的端口
*/
public void start(int listenPort) {
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 HttpClientCodec());
//接收客户端请求,将客户端的请求内容解码
ch.pipeline().addLast("httpRequestDecoder", new HttpRequestDecoder());
//发送响应给客户端,并将发送内容编码
ch.pipeline().addLast("httpResponseEncoder", new HttpResponseEncoder());
ch.pipeline().addLast("httpAggregator", new HttpObjectAggregator(1024*1024));
ch.pipeline().addLast("httpProxyHandler", new HttpProxyHandler());
ch.pipeline().addLast("httpsProxyHandler", new HttpsProxyHandler());
// ch.pipeline().addLast("socksProxyHandler", new SocksProxyHandler());
}
});
Log.i("EasyHttpProxyServer","[EasyHttpProxyServer] proxy server start on {} port"+ listenPort);
ChannelFuture f = b
.bind(listenPort)
.sync();
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
七、实现vpnService 然后在Builder 中绑定代理服务器,启动vpn 即可绑定系统的代理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ProxyInfo proxyInfo = ProxyInfo.buildDirectProxy("localhost", 12000, IgnoreHost.getInstance().getHostList());
// ProxyInfo proxyInfo = ProxyInfo.buildPacProxy(Uri.parse("http://10.10.1.44:8089/js/test.pac"));
vpnBuilder.setHttpProxy(proxyInfo);
}
代理服务建议独立进程存在,这样不影响VPN进程,下一遍继续分享Android 的VPN如何创建和使用