Android拦截并获取WebView内部POST请求参数

最开始想到的方案是直接拦截H5中所有的请求:

webView.setWebViewClient(new WebViewClient() {
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

    }
});

但是通过此方法只能获取Get请求的参数(因为参数直接拼在URL链接中),对于Post请求的参数无可奈何

参考:https://github.com/KonstantinSchubert/request_data_webviewclient
参考:https://github.com/KeejOow/android-post-webview
参考:https://www.cnblogs.com/lanxingren/archive/2019/04/12/10697106.html

public class MainActivity extends AppCompatActivity {

    private String mUrl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        StatusBarUtil.setLightMode(this);
        StatusBarUtil.setTransparent(this);
        StatusBarUtil.setColor(this, Color.WHITE, 0);
        mLinearLayout = this.findViewById(R.id.container);
        mUrl = "https://checkout.appblog.cn/xxxxxx";
        initView();
    }

    private void initView() {
        Map<String, String> webviewHeader =new HashMap<>();
        //此方法无效
        webviewHeader.put("Referer", "www.appblog.cn");
        webviewHeader.put("referrer", "www.appblog.cn");

        WebView webView = findViewById(R.id.web_view);
        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setAppCacheEnabled(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setSavePassword(true);
        webSettings.setSupportZoom(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
        webSettings.setUseWideViewPort(true);

        webView.setWebViewClient(new WriteHandlingWebViewClient(webView) {
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WriteHandlingWebResourceRequest request) {
                String url = request.getUrl().toString();
                Log.i("yezhou", request.getUrl().toString());
                if (url.contains("gateway")) {
                    return getNewResponse(request);
                } else {
                    return super.shouldInterceptRequest(view, request);
                }
            }

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                handler.proceed();
            }
        });

        webView.loadUrl(mUrl, webviewHeader);
    }

    private WebResourceResponse getNewResponse(WriteHandlingWebResourceRequest resourceRequest) {
        try {
            OkHttpClient httpClient = new OkHttpClient();
            String url = resourceRequest.getUrl().toString();

            Request.Builder builder = new Request.Builder();
            //Request request = new Request.Builder()
            builder.url(url.trim());
            if (url.contains("gateway")) {
                builder.addHeader("referrer", "testpay.th.jiaozifin.com"); // Example header
            }
            if ("POST".equalsIgnoreCase(resourceRequest.getMethod())) {
                builder.post(RequestBody.create(resourceRequest.getAjaxData().getBytes()));
            }
            Request request = builder.build();

            Response response = httpClient.newCall(request).execute();

            return new WebResourceResponse(
                    null,
                    response.header("content-encoding", "utf-8"),
                    response.body().byteStream()
            );

        } catch (Exception e) {
            return null;
        }

    }
}

AjaxInterceptJavascriptInterface.java

import java.io.IOException;
import org.jsoup.Jsoup;

import android.content.Context;
import android.webkit.JavascriptInterface;

class AjaxInterceptJavascriptInterface {

    private static String interceptHeader = null;
    private WriteHandlingWebViewClient mWebViewClient = null;

    public AjaxInterceptJavascriptInterface(WriteHandlingWebViewClient webViewClient) {
        mWebViewClient = webViewClient;
    }

    public static String enableIntercept(Context context, byte[] data) throws IOException {
        if (interceptHeader == null) {
            interceptHeader = new String(
                    Utils.consumeInputStream(context.getAssets().open("interceptheader.html"))
            );
        }

        org.jsoup.nodes.Document doc = Jsoup.parse(new String(data));
        doc.outputSettings().prettyPrint(true);

        // Prefix every script to capture submits
        // Make sure our interception is the first element in the
        // header
        org.jsoup.select.Elements element = doc.getElementsByTag("head");
        if (element.size() > 0) {
            element.get(0).prepend(interceptHeader);
        }

        String pageContents = doc.toString();
        return pageContents;
    }

    @JavascriptInterface
    public void customAjax(final String ID, final String body) {
        mWebViewClient.addAjaxRequest(ID, body);
    }
}

Utils.java

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

class Utils {

    static byte[] consumeInputStream(InputStream inputStream) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        for (int count; (count = inputStream.read(buffer)) != -1; ) {
            byteArrayOutputStream.write(buffer, 0, count);
        }
        return byteArrayOutputStream.toByteArray();
    }
}

WriteHandlingWebResourceRequest.java

import android.net.Uri;
import android.webkit.WebResourceRequest;
import java.util.Map;

public class WriteHandlingWebResourceRequest implements WebResourceRequest {
    final private Uri uri;
    final private WebResourceRequest originalWebResourceRequest;
    final private String requestBody;

    WriteHandlingWebResourceRequest(
            WebResourceRequest originalWebResourceRequest,
            String requestBody,
            Uri uri
    ){
        this.originalWebResourceRequest = originalWebResourceRequest;
        this.requestBody = requestBody;
        if (uri!=null) {
            this.uri = uri;
        }else{
            this.uri = originalWebResourceRequest.getUrl();
        }
    }

    @Override
    public Uri getUrl() {
        return this.uri;
    }

    @Override
    public boolean isForMainFrame() {
        return originalWebResourceRequest.isForMainFrame();
    }

    @Override
    public boolean isRedirect() {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean hasGesture() {
        return originalWebResourceRequest.hasGesture();
    }

    @Override
    public String getMethod() {
        return originalWebResourceRequest.getMethod();
    }

    @Override
    public Map<String, String> getRequestHeaders() {
        return originalWebResourceRequest.getRequestHeaders();
    }
    public String getAjaxData(){
        return requestBody;
    }

    public boolean hasAjaxData(){
        return requestBody != null;
    }
}

WriteHandlingWebViewClient.java

import android.content.Context;
import android.net.Uri;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

public class WriteHandlingWebViewClient extends WebViewClient {

    private final String MARKER = "AJAXINTERCEPT";
    private Map<String, String> ajaxRequestContents = new HashMap<>();

    public WriteHandlingWebViewClient(WebView webView) {
        AjaxInterceptJavascriptInterface ajaxInterface = new AjaxInterceptJavascriptInterface(this);
        webView.addJavascriptInterface(ajaxInterface , "interception");
    }

    /*
    This here is the "fixed" shouldInterceptRequest method that you should override.
    It receives a WriteHandlingWebResourceRequest instead of a WebResourceRequest.
     */
    public WebResourceResponse shouldInterceptRequest(
            final WebView view,
            WriteHandlingWebResourceRequest request
    ){
        return null;
    }

    @Override
    public final WebResourceResponse shouldInterceptRequest(
            final WebView view,
            WebResourceRequest request
    ) {

        String requestBody = null;
        Uri uri = request.getUrl();
        if (isAjaxRequest(request)){
            requestBody = getRequestBody(request);
            uri = getOriginalRequestUri(request, MARKER);
        }
        WebResourceResponse webResourceResponse =  shouldInterceptRequest(
                view,
                new WriteHandlingWebResourceRequest(request, requestBody, uri)
        );
        if (webResourceResponse == null){
            return webResourceResponse;
        } else {
            return injectIntercept(webResourceResponse, view.getContext());
        }
    }

    void addAjaxRequest(String id, String body){
        ajaxRequestContents.put(id, body);
    }

    private String getRequestBody(WebResourceRequest request){
        String requestID = getAjaxRequestID(request);
        return  getAjaxRequestBodyByID(requestID);
    }

    private boolean isAjaxRequest(WebResourceRequest request){
        return request.getUrl().toString().contains(MARKER);
    }

    private String[] getUrlSegments(WebResourceRequest request, String divider){
        String urlString = request.getUrl().toString();
        return urlString.split(divider);
    }

    private String getAjaxRequestID(WebResourceRequest request) {
        return getUrlSegments(request, MARKER)[1];
    }

    private Uri getOriginalRequestUri(WebResourceRequest request, String marker){
        String urlString = getUrlSegments(request, marker)[0];
        return Uri.parse(urlString);
    }

    private String getAjaxRequestBodyByID(String requestID){
        String body = ajaxRequestContents.get(requestID);
        ajaxRequestContents.remove(requestID);
        return body;
    }

    private WebResourceResponse injectIntercept(WebResourceResponse response, Context context){
        String encoding = response.getEncoding();
        String mime = response.getMimeType();
        InputStream responseData = response.getData();
        InputStream injectedResponseData = injectInterceptToStream(
                context,
                responseData,
                mime,
                encoding
        );
        return new WebResourceResponse(mime, encoding, injectedResponseData);
    }

    private InputStream injectInterceptToStream(
            Context context,
            InputStream is,
            String mime,
            String charset
    ) {
        try {
            byte[] pageContents = Utils.consumeInputStream(is);
            if (mime.equals("text/html")) {
                pageContents = AjaxInterceptJavascriptInterface
                        .enableIntercept(context, pageContents)
                        .getBytes(charset);
            }

            return new ByteArrayInputStream(pageContents);
        } catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
    }
}

版权声明:
作者:Joe.Ye
链接:https://www.appblog.cn/index.php/2023/03/30/android-intercepts-and-obtains-webview-internal-post-request-parameters/
来源:APP全栈技术分享
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
打赏
海报
Android拦截并获取WebView内部POST请求参数
最开始想到的方案是直接拦截H5中所有的请求: webView.setWebViewClient(new WebViewClient() { @Override public WebResourceResponse shouldInterce……
<<上一篇
下一篇>>
文章目录
关闭
目 录