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;
}
}
}