Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using webview and proxy with authentication

I need to load some url in WebView that is unavailable in one country, so I tried to use a proxy with WebView. I've found one solution on SO (https://stackoverflow.com/a/18453384/7478869), and it works with proxy without authentication. But I need to set the proxy with user and password.

Some approaches, that didn't help:

1) Load url with headers

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

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

2) Add password and user in setProxy method (full code below):

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

But I am still getting this error

[WARNING:http_network_transaction.cc(339)] Blocked proxy response with status 407 to CONNECT request for example.com:443

And in WebView: ERR_TUNNEL_CONNECTION_FAILED

Full code:

public class MainActivity extends AppCompatActivity {
public static final String LOG_TAG = "Main";
WebView webview;
String applicationClassName="android.app.Application";
String user = "web";
String password = "password";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    webview = findViewById(R.id.webview);
    webview.getSettings().setJavaScriptEnabled(true);

    String databasePath = webview.getContext().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
    webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
    webview.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
    webview.getSettings().setDatabaseEnabled(true);
    webview.getSettings().setDatabasePath(databasePath);
    webview.getSettings().setAppCacheMaxSize(5 * 1048576);
    webview.getSettings().setAppCachePath(databasePath);
    webview.getSettings().setAppCacheEnabled(true);
    webview.getSettings().setLoadWithOverviewMode(true);
    webview.getSettings().setDomStorageEnabled(true);
    webview.getSettings().setJavaScriptEnabled(true);
    webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
    webview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);

    setProxy(webview, "85.10.195.100", 443, applicationClassName, user, password);
    webview.setWebViewClient(new ProxyAuthWebViewClient(user,password));
    loadUrl(webview,"https://example.com",user,password);

}

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

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


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


// from https://stackoverflow.com/questions/19979578/android-webview-set-proxy-programatically-kitkat
@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;
  }
}

How to use a proxy with authentication in WebView properly?

like image 341
Big Coach Avatar asked Mar 02 '19 10:03

Big Coach


1 Answers

Ok, so I Found the answer. Need to add onReceivedHttpAuthRequest in WebViewClient

 @Override
        public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
            handler.proceed(user,password);
        }
like image 85
Big Coach Avatar answered Sep 23 '22 23:09

Big Coach