Android 拦截WebView中发生的所有网络通信

Android 拦截WebView中发生的所有网络通信,android,webview,android-webview,okhttp3,intercept,Android,Webview,Android Webview,Okhttp3,Intercept,我希望能够站在WebView和服务器之间,观察和修改进出的任何数据。这需要在主机应用程序中进行 我想截取所有内容——而不仅仅是表示页面加载的简单GET请求。我希望能够看到所有请求,而不考虑动词,例如由HTML表单触发的POST请求。我想看看重定向 有哪些用例: 将附加头附加到请求 从响应中读取标题 针对每个请求的证书固定 我已经阅读了所有我能在网上找到的东西。我的结论是,没有任何自定义WebView实现可以插入到我的应用程序中并满足我的需要。到目前为止,我最接近的方法是使用WebViewCl

我希望能够站在WebView和服务器之间,观察和修改进出的任何数据。这需要在主机应用程序中进行

我想截取所有内容——而不仅仅是表示页面加载的简单GET请求。我希望能够看到所有请求,而不考虑动词,例如由HTML表单触发的POST请求。我想看看重定向

有哪些用例:

  • 将附加头附加到请求
  • 从响应中读取标题
  • 针对每个请求的证书固定
我已经阅读了所有我能在网上找到的东西。我的结论是,没有任何自定义WebView实现可以插入到我的应用程序中并满足我的需要。到目前为止,我最接近的方法是使用WebViewClient.shouldInterceptRequest。我自己在定制的HTTP客户端(OkHttpClient)中执行请求,并将响应返回到WebView

这不起作用的一些关键原因:无法处理在HTML中一致执行的请求;即使我成功地处理了它们,当它是一个帖子时,我也无法抓住请求主体;我无法处理重定向。最后一点在中展开

。 目前,我正在询问是否可以将HTTP客户端插入其他地方,这样就不会干扰WebView的功能

这是我的密码:

WebViewClient:

@Override
public WebResourceResponse shouldInterceptRequest(final WebView view, String url) {
    Log.d(Constants.Tags.WEBVIEW_CLIENT, "WV REQUEST (OLD) " + url);
    return processRequest(url);
}

@Override
@TargetApi(21)
public WebResourceResponse shouldInterceptRequest(final WebView view, WebResourceRequest
        interceptedRequest) {
    Log.d(Constants.Tags.WEBVIEW_CLIENT, "WV REQUEST (NEW) " + interceptedRequest.getUrl
            ().toString() + " " + interceptedRequest.getMethod());
    return processRequest(interceptedRequest);
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private WebResourceResponse processRequest(WebResourceRequest ir) {
    if (!"GET".equals(ir.getMethod())) {
        Log.d(Constants.Tags.WEBVIEW_CLIENT, "IGNORING " + ir.getMethod() + " " + ir.getUrl());
        return null;
    }
    return processRequest(ir.getUrl().toString());
}

private WebResourceResponse processRequest(String url) {
    Request request = new Request.Builder().url(url).build();
    try {
        return processResponse(okHttpClient.newCall(request).execute());
    } catch (SSLHandshakeException e) {
        Log.d(Constants.Tags.WEBVIEW_CLIENT, "SSLHandshakeException: " + e.getMessage());
    } catch (IOException e) {
        Log.d(Constants.Tags.WEBVIEW_CLIENT, "IOException: " + e.getMessage());
        e.printStackTrace();
    }
    return null;
}

private WebResourceResponse processResponse(Response response) {
    String contentType = response.body().contentType().toString();

    if (contentType != null) {
        String mimeType = contentType;

        if (contentType.contains(";")) {
            mimeType = contentType.split(";")[0].trim();
        }

        WebResourceResponse webResourceResponse = new WebResourceResponse(mimeType, response
                .header("content-encoding", "utf-8")
                , response.body().byteStream());

        if (Build.VERSION.SDK_INT > 21) {
            webResourceResponse.setResponseHeaders(convertHeaders(response.headers()));
            webResourceResponse.setStatusCodeAndReasonPhrase(response.code(),"whatever");
        }

        return webResourceResponse;
    }
    return null;
}

private Map<String, String> convertHeaders(Headers headers) {
    Map<String, String> map = new HashMap<>();
    for (int i = 0; i < headers.size(); i++) {
        map.put(headers.name(i), headers.value(i));
    }
    return map;
}
@覆盖
公共WebResourceResponse应InterceptRequest(最终WebView视图,字符串url){
Log.d(Constants.Tags.WEBVIEW_客户端,“WV请求(旧)”+url);
返回processRequest(url);
}
@凌驾
@塔吉塔皮(21)
公共WebResourceResponse应该InterceptRequest(最终WebView视图,WebResourceRequest
截取请求){
Log.d(Constants.Tags.WEBVIEW_客户端,“WV请求(新)”+interceptedRequest.getUrl
().toString()+“”+interceptedRequest.getMethod());
返回processRequest(interceptedRequest);
}
@RequiresApi(api=Build.VERSION\u code.LOLLIPOP)
私有WebResourceResponse processRequest(WebResourceRequest ir){
如果(!“GET”.equals(ir.getMethod())){
Log.d(Constants.Tags.WEBVIEW_客户端,“忽略”+ir.getMethod()+“”+ir.getUrl());
返回null;
}
返回processRequest(ir.getUrl().toString());
}
私有WebResourceResponse processRequest(字符串url){
Request Request=newrequest.Builder().url(url.build();
试一试{
返回processResponse(okHttpClient.newCall(request.execute());
}捕获(SSLhandE){
Log.d(Constants.Tags.WEBVIEW_客户端,“SSLHandshakeException:+e.getMessage());
}捕获(IOE异常){
Log.d(Constants.Tags.WEBVIEW_客户端,“IOException:+e.getMessage());
e、 printStackTrace();
}
返回null;
}
私有WebResourceResponse processResponse(响应){
字符串contentType=response.body().contentType().toString();
if(contentType!=null){
字符串mimeType=contentType;
if(contentType.contains(“;”){
mimeType=contentType.split(“;”)[0]。trim();
}
WebResourceResponse WebResourceResponse=新的WebResourceResponse(mimeType,response
.header(“内容编码”、“utf-8”)
,response.body().byteStream());
如果(Build.VERSION.SDK_INT>21){
webResourceResponse.setResponseHeaders(convertHeaders(response.headers());
webResourceResponse.setStatusCodeAndReasonPhrase(response.code(),“无论什么”);
}
返回webResourceResponse;
}
返回null;
}
私有映射头(头){
Map Map=newhashmap();
对于(int i=0;i
OkHttpClient:

public static OkHttpClient getNewHttpClient() {

    CertificatePinner.Builder certPinnerBuilder = new CertificatePinner.Builder();

    for (Map.Entry<String, String> entry : THUMB_PRINTS.entrySet()) {
        certPinnerBuilder.add(entry.getKey(), entry.getValue());
    }

    CertificatePinner certPinner = certPinnerBuilder.build();

    OkHttpClient.Builder client = new OkHttpClient.Builder()
            .certificatePinner(certPinner)
            .addNetworkInterceptor(new AdditionalHeaderInterceptor())
            .followRedirects(false)
            .followSslRedirects(false)
            .retryOnConnectionFailure(true)
            .connectTimeout(Constants.REQUEST_TIMEOUT, TimeUnit.SECONDS)
            .readTimeout(Constants.REQUEST_TIMEOUT, TimeUnit.SECONDS)
            .writeTimeout(Constants.REQUEST_TIMEOUT, TimeUnit.SECONDS)
            .cache(null);

    return enableTls12OnPreLollipop(client).build();
}

public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
    if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
        try {
            SSLContext sc = SSLContext.getInstance("TLSv1.2");
            sc.init(null, null, null);
            client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));

            ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                    .tlsVersions(TlsVersion.TLS_1_2)
                    .build();

            List<ConnectionSpec> specs = new ArrayList<>();
            specs.add(cs);
            specs.add(ConnectionSpec.COMPATIBLE_TLS);
            specs.add(ConnectionSpec.CLEARTEXT);

            client.connectionSpecs(specs);
        } catch (Exception exc) {
            Log.e(Constants.Tags.GENERAL, "Error while setting TLS 1.2", exc);
        }
    }

    return client;
}
publicstaticokhttpclient getNewHttpClient(){
CertificatePaner.Builder certPinnerBuilder=新的CertificatePaner.Builder();
对于(Map.Entry:THUMB\u PRINTS.entrySet()){
添加(entry.getKey(),entry.getValue());
}
CertificatePaner certPinner=certPinnerBuilder.build();
OkHttpClient.Builder client=新的OkHttpClient.Builder()
.CertificatePaner(certPinner)
.addNetworkInterceptor(新的AdditionalHeaderInterceptor())
.followRedirects(false)
.followslRedirects(false)
.retryOnConnectionFailure(真)
.connectTimeout(常数.REQUEST\u TIMEOUT,TimeUnit.SECONDS)
.readTimeout(常数.REQUEST\u TIMEOUT,TimeUnit.SECONDS)
.writeTimeout(常数.REQUEST\u超时,时间单位.SECONDS)
.cache(空);
返回enabletls12onprellollipop(client.build();
}
公共静态OkHttpClient.Builder启用TLS12ONPLELOLLIPOP(OkHttpClient.Builder客户端){
if(Build.VERSION.SDK_INT>=16&&Build.VERSION.SDK_INT<22){
试一试{
SSLContext sc=SSLContext.getInstance(“TLSv1.2”);
sc.init(null,null,null);
client.sslSocketFactory(新的Tls12SocketFactory(sc.getSocketFactory());
ConnectionSpec cs=新的ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.TlsVersion(TlsVersion.TLS_1_2)
.build();
列表规格=新的ArrayList();
增加规格(cs);
规格添加(连接规格兼容);
规范添加(连接规范明文);
客户连接规范(规范);
}捕获(异常exc){
Log.e(Constants.Tags.GENERAL,“设置TLS 1.2时出错”,exc);
}
}
返回客户;
}

你为什么要把问题贴两次@史蒂文:这个问题试图解决我的实际问题:拦截所有WebView通信,而另一个问题集中在处理WebView中的重定向。相同的代码片段,不同的焦点。