How to handle Basic Authentication in WebView

后端 未结 5 1606
無奈伤痛
無奈伤痛 2020-11-30 07:47

I\'ve created an application which loads a WebView. In order to login, the website requires basic authentication. When i try to access the website via the default browser, I

相关标签:
5条回答
  • 2020-11-30 08:11

    Use this function onReceivedHttpAuthRequest. It Notifies the host application that the WebView received an HTTP authentication request. The host application can use the supplied HttpAuthHandler to set the WebView's response to the request. The default behavior is to cancel the request.

    Below is the sample written in Kotlin

     override fun onReceivedHttpAuthRequest(
                view: WebView,
                handler: HttpAuthHandler,
                host: String,
                realm: String
            ) {
                handler.proceed("username", "password")
            }
    

    Check the Documentation here

    0 讨论(0)
  • 2020-11-30 08:15

    I know this is old, but adding another approach here as the ones above (using the onReceivedHttpAuthRequest) didn't work for me. Specifically some web pages don't prompt the user for Basic Authentication, they just redirect to the login screen. In my testing, Grafana happens to have this behavior by default.

    So instead I added a HTTP Basic auth header using the below code:

    HashMap<String, String> headers = new HashMap<>();
    String basicAuthHeader = android.util.Base64.encodeToString((username + ":" + password).getBytes(), android.util.Base64.NO_WRAP);
    headers.put("Authorization", "Basic " + basicAuthHeader);
    webView.loadUrl(url, headers);
    

    This should bypass the auth prompt and just set the credentials automatically. Hope this helps someone looking for another solution.

    Also, for anyone hoping to use the URL pattern of "https://username:password@url" - this is deprecated and I think most browsers will ignore it and throw away the credentials.

    edit

    I've come to the realization my above solution only works for the first request, and no subsequent requests after the page is loaded (i.e. for any further resources loaded).

    In order for all subsequent requests to work, I had to override the following function for the web client:

    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request){
        if (username != null && password != null && username.length() > 0 && password.length() > 0) {
            try {
                OkHttpClient client = new OkHttpClient();
                Request.Builder newRequestBuilder = new Request.Builder()
                        .url(request.getUrl().toString());
    
                // add headers
                Map<String, String> currentHeaders = request.getRequestHeaders();
                for (String header : currentHeaders.keySet()){
                    newRequestBuilder.addHeader(header, currentHeaders.get(header));
                }
                String basicAuthHeader = android.util.Base64.encodeToString((username + ":" + password).getBytes(), android.util.Base64.NO_WRAP);
                newRequestBuilder.addHeader("Authorization", "Basic "+basicAuthHeader);
    
                //make request
                Request newRequest = newRequestBuilder.build();
                Response response = client.newCall(newRequest).execute();
    
                // respond with content
                return new WebResourceResponse(response.header("content-type"), response.header("content-encoding", "utf-8"), response.body().byteStream());
            } catch (Exception e) {
                // if an exception occurs, just make a default request
                return null;
            }
        } else {
            // if no username and password, then make a default request
            return null;
        }
    }
    

    Unfortunately this code will not intercept POST requests, only GET requests, which limits what you can do with it.

    Further, the above code should have some logic added to verify the domain of the initial request matches the domain of the intercepted requests before attaching the Authorization headers. Otherwise the Authorization header will be sent with all requests which could unintentionally send it to a different domain (e.g. loading resources from a CDN).

    0 讨论(0)
  • 2020-11-30 08:21
    public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
         handler.proceed("USERNAME", "PASSWORD");
    }
    

    I think it helpful about the 401 error on WebView.

    0 讨论(0)
  • 2020-11-30 08:26

    The website requires authentication.

    First you react to the error:

    webview.setWebViewClient(new WebViewClient() {
       public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
         if (errorCode == 401) {
             // show alert to enter username and password
             // then when those are entered in the alert,
             //     set it through the setHttpAuthUsernamePassword(...) shown below
             //     and then reload the site
         }
       }
     });
    

    Use this function to set user and password: WebView.setHttpAuthUsernamePassword()

    webview.setHttpAuthUsernamePassword(host, realm, username, password);
    

    All are strings. See the link above for more information on what host and realm means.

    Solution found here: Supply some basic auth credentials to a WebView?

    0 讨论(0)
  • 2020-11-30 08:36

    I reckon a more elegant solution than the one Patrick describes would be to use the onReceivedHttpAuthRequest method of WebViewClient as described here: http://www.mail-archive.com/android-developers@googlegroups.com/msg30468.html

    @Override
    public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
        handler.proceed("username", "password");
    }
    
    0 讨论(0)
提交回复
热议问题