netty 应用Android端的代理请求(系统级代理)

一、导入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如何创建和使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尚思app

您的鼓励是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值