Android WebView intercepts post request parameter instances by dynamically modifying js

  • 2021-11-30 01:24:05
  • OfStack

Requirement background:

It is necessary to intercept the data submitted by the user when the user clicks the submit button.

Problems encountered:

1. The page is not made by your own front end, and the code in the page cannot be modified

2. The request to be intercepted is not an get request, but an post request (the difficulty is that if the intercepted request is an get request, I only need to get url and take out the parameter key value pairs spliced later, but the parameter key value of post request is invisible to us. . . )

Key points of solution:

Override the shouldInterceptRequest method of webViewClient

1. This method appeared after API21, and there is an outdated method to rewrite, don't forget!

2. When loading the web page, all the resources will pass through the method of shouldInterceptRequest. We can get the URL and resource files you want to get information through shouldInterceptRequest and package grabbing tools (Fidder, Charles)

3. This method is executed on sub-threads. If you want to update UI, remember to switch threads

Solution:

I found two solutions here (there is always one for you)

Solution A: Suitable for those who are proficient in js

1. Intercept the click event of the button on the page and replace the click event operation


$('#J_submit').off('click'); //1. Will id For J_submit Button click event closes 
$('#J_submit').on('click',function(){ //2. Will id For J_submit The button click event of reopens , And execute function The contents of the 
 if ($(this).hasClass("btn-disabled")) { // -----   Here is the original page code , Do not explain   -----
    return;
   }
 
   try {
    trackDealerEvent('dlr_order_page_form_submit_click', {
     'esfrom': _mediaId,
     'business': 'songshu',
     'series': _seriesId,
     'city': _cityId
    });
   } catch (e) {
    console.log(e);
   }   // -----   Here is the original page code , Do not explain   -----
 var pageFormData = validateAllField(alertDiv);
 if (pageFormData) {  //3. Get the data in the page 
  $.ajax({  //4.ajax Upload the method to the server 
   url: 'https://gouche.jxedt.com/gouche/clue/submit',
   data: {
   cityid: _cityId,
   brandid: _brandId,
   seriesid: _seriesId,
   classesid: _specId,
   name: $("[name='userName']").val(),
   phone: $('#phoneNumber').val(),
   type: 4
   }
  });
  postOrder(pageFormData);
 }
})

2. Load 1 segment of js code dynamically


mCommonWebView.setCommonWebViewClient(new CommonWebViewClient() { // Add a custom  WebViewClient
 @Override
 public void onPageFinished(WebView view, String url) { // Rewrite onPageFinished Method 
  super.onPageFinished(view, url);
  // Request js Web site of 
  runRemoteJs(Constant.QueryCarPrice.loadJsUrl_CarHome);
 }
 
 private void runJs(String remoteJs){ // Put the obtained js Code to the current Web page 
  if(TextUtils.isEmpty(remoteJs)) {
   return;
  }
  String js = "javascript:";  // Action : Indicates that the string is followed by js Code 
  js+= "var script = document.createElement('script');"; //  Action : Create script Node 
  js+= "script.type = 'text/javascript';"; 
  js+=remoteJs;
  mCommonWebView.callJsFunction(js); // Loading js Code 
 }
 
 private void runRemoteJs(String url) {// The front end is greatly provided 1 Web site , Inside the website is the above js Code , Get the code from the web page 
  RxRequest<String> request = new RxRequest<String>()
    .setUrl(url)
    .setMethod(Request.Method.GET);
  RxHttpEngineWrapper.commonExec(request)
    .subscribeOn(AndroidSchedulers.mainThread())
    .subscribe(new UtilsRx.DefaultSubscriber<String>(){
   @Override
   public void onNext(String s) {
    runJs(s);
   }
  });
 }
});

3. When the time comes, you only need to greatly modify js in the front-end page

Pit of this scheme:

1. The script code to be loaded cannot contain an script node

2. You cannot have comments in the js code to load

3. The js code 1 to be loaded must be semicolons

* If the above three requirements are not met, the js to be loaded will not execute correctly

Scheme B: Native Android mode, compared with the previous scheme, this scheme is more troublesome

1. Rewrite shouldInterceptRequest to block resources

2. Download the js page of the third party web page (that is, download all the web pages and find the js page of the web request), and modify the js page

3. Load the processed js page locally, and replace the third party js with the local js when loading later. (I will add a bridge to communicate with webview in the local js page.)


// The following are the specific operations , I posted the specific method , If you don't understand it, you can look at the code , I wrote notes 
 
 
// Initialization WebView
private void initWebView() {
 mWebView.getSettings().setDomStorageEnabled(true);
 mWebView.getSettings().setDefaultTextEncodingName("utf-8");
 if(Build.VERSION.SDK_INT >=21){//Added in API level 21
  mWebView.getSettings().setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
 }
 mWebView.getSettings().setJavaScriptEnabled(true);
 mWebView.getSettings().setUseWideViewPort(true); // Settings webview Recommended window to make html Interface adaptive screen 
 mWebView.getSettings().setLoadWithOverviewMode(true);
 
 mWebView.getSettings().setGeolocationEnabled(true);
 
 mWebView.getSettings().setAllowFileAccess(true);
 
 if (Build.VERSION.SDK_INT >= 16) {
  // Shielding Webview Cross-domain vulnerability of 
  mWebView.getSettings().setAllowFileAccessFromFileURLs(false);
  mWebView.getSettings().setAllowUniversalAccessFromFileURLs(false);
 }
 
 mWebView.getSettings().setPluginState(WebSettings.PluginState.ON);
 if (Build.VERSION.SDK_INT >= 11) {
  mWebView.getSettings().setAllowContentAccess(true);
 }
 
 mWebView.loadUrl(currUrl);
 mWebView.setWebViewClient(new MyWebViewClient());
 
 // And js A bridge of communication 
 mWebView.addJavascriptInterface(new StubClass(),"stub");
 
}
 
public class MyWebViewClient extends WebViewClient {
 /* Two shouldInterceptRequest The content in the method body is roughly the same , Because it is demo, I don't have an extraction method either */
 
 @Override
 public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
  // Object of the request parameters obtained by the  Map  Set 
  HashMap<String,String> params;
 
  Uri uri=Uri.parse(url);  // Object corresponding to the URL Uri
 
  if (rightUrl(uri.toString())) {
   /*get Request to get parameters */
   params=paramForGET(uri);
 
   /* Highlights ,post Request to get parameters */
   /*
    *  Get post The idea of requesting parameters is :
    *  The whose URL makes the network request js Code , To this paragraph js Code for replacement 
    *  What I took was to intercept the first 3 That requests data on the website of the party js Resources , Submit the local resource to replace the original resource 
    */
   if (uri.toString().contains("index.js")) {      // Intercept the corresponding under the web page js Resources and replace them 
    try {
     //WebResourceResponse Constructor of 3 Parameter action  String mimeType: Specify the type of replacement resource   String encoding: Character set   InputStream input: Input stream 
     return new WebResourceResponse("application/x-javascript","UTF-8",getAssets().open("index.js"));
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  return super.shouldInterceptRequest(view, url);
 }
 
 //API21 And 21 This method will be supported later 
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 @Override
 public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
  // Object of the request parameters obtained by the  Map  Set 
  HashMap<String,String> params;
 
  String method=request.getMethod();         // How to submit the current URL 
  Map<String, String> requestHeaders = request.getRequestHeaders(); // Get the request header 
  Uri uri=request.getUrl();           // Object corresponding to the URL Uri
 
  if (rightUrl(uri.toString())) {
   /*get Request to get parameters */
   params=paramForGET(uri);
 
   /* Highlights ,post Request to get parameters */
   /*
    *  Get post The idea of requesting parameters is :
    *  The whose URL makes the network request js Code , To this paragraph js Code for replacement 
    *  What I took was to intercept the first 3 That requests data on the website of the party js Resources , Submit the local resource to replace the original resource 
    */
   if (uri.toString().contains("index.js")) {      // Intercept the corresponding under the web page js Resources and replace them 
    try {
     //WebResourceResponse Constructor of 3 Parameter action  String mimeType: Specify the type of replacement resource   String encoding: Character set   InputStream input: Input stream 
     return new WebResourceResponse("application/x-javascript","UTF-8",getAssets().open("index.js"));
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  return super.shouldInterceptRequest(view, request);
 }
 
 private boolean rightUrl(String url){
  if (url.contains(COLLECT_URL))         // Determine if the resource URL is what I need 
   return true;
  return false;
 }
 
 private HashMap<String,String> paramForGET(Uri uri){
  HashMap<String,String> params=new HashMap<>();
 
  Set<String> paramNames = uri.getQueryParameterNames();   // Gets this get All parameter names in the request 
 
  /* I'm filling in all the parameters here , You can filter and filter when you get it */
  for (String param : paramNames) {
   params.put(param,uri.getQueryParameter(param));    // Store key-value pairs 
  }
 
  return params;
 }
}
 
public class StubClass{
 @JavascriptInterface
 public void getData(String json){
  Log.i("xxx","json -> "+json);
 }
}

This is my local js, the original js has been modified, added and Android communication bridge, to intercept data.

Additional knowledge: android WebView uses Post to request and set browser bullets

Attention should be paid here: post request parameters can only be transmitted to byte array, and must be byte array in the form of key-value to string, in which key is the background server receiving key, and the background stipulates that key is what value is, which cannot be changed at will. If there is no key=value format or key is incorrect, the web page cannot be opened without data request.

The following code looks directly at the initWebView () method


package com.xxxxx.xxx.activity.banksign;
 
import org.json.JSONException;
import org.json.JSONObject;
 
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 
import com.xinzong.etc.R;
import com.xinzong.xx.base.BaseGestureActivty;
import com.xinzong.xxx.utils.ShowReloadUtil;
/**
 * 
 * @author 
 *
 */
public class WebViewActivity extends BaseGestureActivty implements OnClickListener{
	
	private ShowReloadUtil reloadUtil;
 
	private String url = "http://120.1.1.1/xx/xxxx";
	private WebView webView;
	private String urlParameter = "";
	
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_sign_webview);
		findViewById(R.id.ibBack).setOnClickListener(this);
 
		// Get the payment parameters passed in 
		urlParameter = getIntent().getStringExtra("urlParameter");
		Log.i("TAG", urlParameter);
		
		// Initialize the reload box 
		reloadUtil = new ShowReloadUtil(this);
		reloadUtil.setReloadView(this, R.id.ll_show_data_mc,
				R.id.rl_reload_parent_mc);
		// Refresh the interface and load webview
		refresh();
		
		
	}
	
	
	 
	 private void refresh() {
	  if(isNetworkConnected()){
	   findView(R.id.webview1).setVisibility(View.VISIBLE);
	   reloadUtil.showDataView();
	   initWebView();
	  }else{
	   findView(R.id.webview1).setVisibility(View.GONE);
	   reloadUtil.showReload();
	  }
 
	 }
	 
	 private void initWebView() {
	  webView = (WebView) findViewById(R.id.webview1);
	  
	  // Initialization webview
	  // Enable support javascript
	  WebSettings settings = webView.getSettings();
	  settings.setJavaScriptEnabled(true);// Support javaScript
	  settings.setDefaultTextEncodingName("utf-8");// Set the default encoding of web pages 
	  settings.setJavaScriptCanOpenWindowsAutomatically(true);
	  Log.d("TAG", "url:"+url);
	  //post Request ( Use key-value pair form, format and get Request 1 Sample, key=value, Multiple use & Connect )
	  urlParameter = "JSONpriKey=" +urlParameter;
	  webView.postUrl(url, urlParameter.getBytes());
//	  webView.loadUrl(url);//get
	  webView.setWebChromeClient(new MyWebChromeClient());//  Set the browser pop-up window  
	  // Covering WebView By default, the 3 The behavior of the browser to open a web page by default, so that the web page can be used with WebView Open 
	  webView.setWebViewClient(new WebViewClient(){
	   @Override
	  public boolean shouldOverrideUrlLoading(WebView view, String url) {
	    // The return value is true Control it when you are WebView Open for false Call the system browser or the 3 Square browser 
	    Log.d("TAG", "url:"+url);
	    view.loadUrl(url);
	   return true;
	  }
	  @Override
	  	public void onPageStarted(WebView view, String url,
	  			Bitmap favicon) {
	  		Log.d("TAG", "onPageStarted--url:"+url);
	  		// After payment is completed , Click back to close the interface 
	  		if(url.endsWith("http://120.1.1.1/xxx/xx/xxx")){
	  			finish();
	  		}
	  		
	  		super.onPageStarted(view, url, favicon);
	  	}
	  
	  
	  @Override
	  	public void onPageFinished(WebView view, String url) {
	  		super.onPageFinished(view, url);
	  		
	  	}
	   
	  });
	  
	  
 
	 }
 
 
 
		@Override
		public void onClick(View v) {
			if (v.getId() == R.id.btnReload) {//  Click   'Reload '
				
				reloadUtil.showClickloadingView();
				Log.d("TAG", "RELOAD");
				if (this.isNetworkConnected()) {
					webView.loadUrl(url);
				} else {
					reloadUtil.showReload();
				}
			}else if(v.getId() == R.id.ibBack){
				if(webView !=null && webView.canGoBack()){
					webView.goBack();
				}else{
					finish();
				}
			}
			
		}
		
		
		@Override
	 public boolean onKeyDown(int keyCode, KeyEvent event) {
	  if(keyCode == KeyEvent.KEYCODE_BACK && webView !=null && webView.canGoBack()){
	   webView.goBack();
	   return true;
	  }
	  
	  return super.onKeyDown(keyCode, event);
	 }
 
	
		
		 /** 
	  *  Browser popup window  
	  * 
	  * @author Administrator 
	  * 
	  */ 
	 final class MyWebChromeClient extends WebChromeClient { 
	  @Override 
	  public boolean onJsConfirm(WebView view, String url, String message, 
	    final JsResult result) { 
	   new AlertDialog.Builder(CTX) 
	     .setTitle("App Titler") 
	     .setMessage(message) 
	     .setPositiveButton(android.R.string.ok, 
	       new DialogInterface.OnClickListener() { 
	        public void onClick(DialogInterface dialog, 
	          int which) { 
	         result.confirm(); 
	        } 
	       }) 
	     .setNegativeButton(android.R.string.cancel, 
	       new DialogInterface.OnClickListener() { 
	        public void onClick(DialogInterface dialog, 
	          int which) { 
	         result.cancel(); 
	        } 
	       }).create().show(); 
	 
	   return true; 
	  } 
	 } 
}

Related articles: