How to using Android Webviews authenticated proxy

As mention in this StackOverFlow, there is no legal or direct way/method to change your webview proxy settings programmatically. But it’s possible to use java reflection to change ProxyHost value from android.net.http.RequestQueue class. It’s private value and there is no setters for it, so reflection seems to be the only possible variant.

Its works for me as using the following modified method, and tested up Android 9.

    @SuppressLint("NewApi")
    @SuppressWarnings("all")
    private static boolean setProxyKKPlus(WebView webView, String host, int port, final String user, final String password, String applicationClassName) {
            Log.d(LOG_TAG, "Setting proxy with >= 4.4 API.");

            Context appContext = webView.getContext().getApplicationContext();
            System.setProperty("http.proxyHost", host);
            System.setProperty("http.proxyPort", port + "");
            System.setProperty("https.proxyHost", host);
            System.setProperty("https.proxyPort", port + "");


            Authenticator.setDefault(
                    new Authenticator() {
                        @Override
                        public PasswordAuthentication getPasswordAuthentication() {
                            return new PasswordAuthentication(
                                    user, password.toCharArray());
                        }
                    }
            );

            System.setProperty("http.proxyUser", user);
            System.setProperty("http.proxyPassword", password);
            System.setProperty("https.proxyUser", user);
            System.setProperty("https.proxyPassword", password );

            try {
                Class applictionCls = Class.forName(applicationClassName);
                Field loadedApkField = applictionCls.getField("mLoadedApk");
                loadedApkField.setAccessible(true);
                Object loadedApk = loadedApkField.get(appContext);
                Class loadedApkCls = Class.forName("android.app.LoadedApk");
                Field receiversField = loadedApkCls.getDeclaredField("mReceivers");
                receiversField.setAccessible(true);
                ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
                for (Object receiverMap : receivers.values()) {
                    for (Object rec : ((ArrayMap) receiverMap).keySet()) {
                        Class clazz = rec.getClass();
                        if (clazz.getName().contains("ProxyChangeListener")) {
                            Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class);
                            Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);

                            onReceiveMethod.invoke(rec, appContext, intent);
                        }
                    }
                }

                Log.d(LOG_TAG, "Setting proxy with >= 4.4 API successful!");
                return true;
            } catch (ClassNotFoundException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (NoSuchFieldException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (IllegalAccessException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (IllegalArgumentException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (NoSuchMethodException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            } catch (InvocationTargetException e) {
                StringWriter sw = new StringWriter();
                e.printStackTrace(new PrintWriter(sw));
                String exceptionAsString = sw.toString();
                Log.v(LOG_TAG, e.getMessage());
                Log.v(LOG_TAG, exceptionAsString);
            }
        return false;

    }
    

Now we need to using above method to set the webview instance proxy, so create setProxy method as below.

    public static boolean setProxy(WebView webview, String host, int port, String applicationClassName, String user, String password) {
        return setProxyKKPlus(webview, host, port, user, password, applicationClassName);
    }

And to load the url into the webview instance we will use the method loadUrl

public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){
        UsernamePasswordCredentials creds= new UsernamePasswordCredentials(proxyUserName, proxyPassword);
        Header credHeader = BasicScheme.authenticate(creds, "UTF-8", true);
        Map<String, String> header = new HashMap<String, String>();
        header.put(credHeader.getName(), credHeader.getValue());
        view.loadUrl(url, header);
    }

In normal browsing scenario, if using authenticated proxy, for the first time you request an URL, there are a pop up form that fires before starting loading the requested URL.
ie Firefox popup form with label “Proxy Authorization Required” and “Description: Authorization is required for access to this proxy” or some thing like the below image.

and so we need to enter the correct authentication information username and password, then we can pass through this proxy and loading the URL.

Now we will do that programatically by extend WebViewClient as following and using onReceivedHttpAuthRequest method

    class ProxyAuthWebViewClient extends WebViewClient {
        String proxyUserName;
        String proxyPassword;
        public ProxyAuthWebViewClient(String proxyUserName, String proxyPassword){
            this.proxyUserName = proxyUserName;
            this.proxyPassword = proxyPassword;
        }
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            loadUrl(view, url, proxyUserName, proxyPassword);
            return true ;
        }

        @Override
        public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
            view.loadUrl(url); //reload the url.
            handler.proceed(user,password);
        }

    }

Then the main entry method will be like

public class MainActivity extends AppCompatActivity {

--------------------------
--------------------------

 WebView webview;
 String applicationClassName="android.app.Application";
 String user = "passnews010";
 String password = "@ProxyHP4TH1048";
 String proxyServer = "213.219.38.192";
 int proxyPort = 8080;
 
 -------------------------
 -------------------------
 
     protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
 -------------------------
 -------------------------
        //Set Webview instance
        webview=findViewById(R.id.webview);



        WebSettings webSettings = webview.getSettings();
        CookieManager.getInstance().setAcceptCookie(true);
        webview.getSettings().setJavaScriptEnabled(true);
        webview.getSettings().setSupportZoom(true);
        
        
        
        setProxy(webview, proxyServer, proxyPort, applicationClassName, user, password);
        
        webview.setWebViewClient(new ProxyAuthWebViewClient(user,password));
        
        loadUrl(webview,"https://ipinfo.io/",user,password);
 
 -------------------------
 -------------------------
 }

In the Android Studio build.gradle(Module:app) file we need to add useLibrary 'org.apache.http.legacy' as

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "news.pass.app.alummah"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        manifestPlaceholders = [
                onesignal_app_id: '86e59daf-b90c-4849-9603-d822d564523c',
                // Project number pulled from dashboard, local value is ignored.
                onesignal_google_project_number: 'REMOTE'
        ]
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    useLibrary 'org.apache.http.legacy'

}

Resources:

You may also like...

Leave a Reply