在Galaxy Tab 10.1上运行3.1(Honeycomb)
无论使用多种方法,我都无法重置WebView的基本身份验证用户名和密码。我可以重置这些值的唯一方法是重新启动应用程序。我已经四处搜索,还没有找到解决方案,甚至挖到Android源代码。
此代码来自每次要显示需要基本身份验证的网页时创建的活动。有些部分应该没有任何效果,但是试图摆脱挫败感。即使我退出此活动(然后销毁)并使用我的主活动的意图重新启动它,基本的身份验证信息仍然存在,并且WebViewClient中的onReceivedHttpAuthRequest永远不会再次执行。
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.base_simple_v01);
findViewById(R.id.lyt_bsv01_layout).setBackgroundColor(0xFF000000);
baseContainer = (ViewGroup) findViewById(R.id.lyt_bsv01_baseContainer);
statusProgressBar = (ProgressBar) findViewById(R.id.lyt_bsv01_statusProgress);
resultNotificationTextView = (TextView) findViewById(R.id.lyt_bsv01_resultNotification);
// -- Attempt to prevent and clear WebView cookies
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
cookieManager.removeSessionCookie();
cookieManager.setAcceptCookie(false);
// -- Attempt to clear WebViewDatabase
WebViewDatabase.getInstance(this).clearHttpAuthUsernamePassword();
WebViewDatabase.getInstance(this).clearUsernamePassword();
WebViewDatabase.getInstance(this).clearFormData();
// -- Brute force attempt to clear WebViewDatabase - didn't work
//deleteDatabase("webview.db");
//deleteDatabase("webviewCache.db");
LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
networkWebView = (WebView)vi.inflate(R.layout.social_connect, baseContainer, false);
// -- Removes white flickering in Honeycomb WebView page loading.
networkWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
networkWebView.getSettings().setJavaScriptEnabled(true);
networkWebView.getSettings().setSavePassword(false);
networkWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
networkWebView.clearSslPreferences();
networkWebView.setWebViewClient(mLocalDataRequester.endorseBackendAuthWebViewClient(
new BackendAuthWebViewClient() {
@Override
public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
Toast.makeText(getApplicationContext(), "AUTH REQUESTED", Toast.LENGTH_SHORT).show();
super.onReceivedHttpAuthRequest (view, handler, host, realm);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
Toast.makeText(getApplicationContext(), "SSL ERROR", Toast.LENGTH_SHORT).show();
super.onReceivedSslError(view, handler, error);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
statusProgressBar.setVisibility(View.VISIBLE);
networkWebView.setVisibility(View.INVISIBLE);
}
@Override
public void onPageFinished(WebView view, String url) {
statusProgressBar.setVisibility(View.INVISIBLE);
networkWebView.setVisibility(View.VISIBLE);
}
})
);
baseContainer.addView(networkWebView);
networkWebView.setVisibility(View.INVISIBLE);
networkWebView.setBackgroundColor(0x00000000);
clearWebView();
}
private void clearWebView() {
networkWebView.loadData("", "text/html", "utf-8");
//networkWebView.clearView();
networkWebView.clearCache(false);
networkWebView.clearCache(true);
networkWebView.clearFormData();
networkWebView.clearHistory();
networkWebView.clearCache(true);
networkWebView.clearMatches();
networkWebView.freeMemory();
}
@Override
public void onResume() {
super.onResume();
networkWebView.loadUrl(mBackendNetworkConnectUrl);
WebViewDatabase.getInstance(this).clearHttpAuthUsernamePassword();
}
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(getApplicationContext(), "Destruction", Toast.LENGTH_SHORT).show();
networkWebView.destroy();
}
这是一个使用基本身份验证凭据初始化的WebViewClient子类。我已经验证了在进行身份验证时用户名和密码会发生变化。
public class BackendAuthWebViewClient extends WebViewClient {
private AuthenticateData mAuthenticateData = null;
public BackendAuthWebViewClient() {
}
public BackendAuthWebViewClient(AuthenticateData authenticateData) {
this.mAuthenticateData = authenticateData;
}
@Override
public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm){
handler.proceed(mAuthenticateData.mUserId, mAuthenticateData.mUserPassword);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
public void setAuthenticatedData(AuthenticateData authenticateData) {
this.mAuthenticateData = authenticateData;
}
}
我试过以下无济于事:
Android WebView - reset HTTP session
Clearing user's Facebook session in Webview
Make Android WebView not store cookies or passwords
Android WebView Cookie Problem
这很有趣,但蛮力的必要性令人失望。虽然我接下来会尝试。
编辑:无效。
答案 0 :(得分:1)
我很确定它是WebViewDatabase.getInstance(this).clearHttpAuthUsernamePassword();
中的一个错误,
因为
deleteDatabase("webview.db");
为我做了诀窍。
答案 1 :(得分:1)
答案 2 :(得分:0)
虽然它没有解决原始重置WebView基本身份验证问题,但我将其用作解决方法。使用此SO作为参考:
此解决方案使用HttpClient请求(最好在另一个线程或AsyncTask中以避免ANR - 应用程序没有响应),然后将该响应加载到WebView中。由于我需要与加载页面上的链接进行交互,因此我需要使用loadDataWithBaseURL。
对于这个答案,我在Apache License 2.0.
下授权了以下所有代码HttpClient代码 - 最好用于另一个线程或AsyncTask。需要定义或删除变量authenticateData,method,url和nameValuePairs。
public String send() {
try {
// -- Create client.
HttpParams httpParameters = new BasicHttpParams();
// Set the timeout in milliseconds until a connection is established.
int timeoutConnection = 10000;
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
// Set the default socket timeout (SO_TIMEOUT)
// in milliseconds which is the timeout for waiting for data.
int timeoutSocket = 10000;
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
DefaultHttpClient httpClient = new DefaultHttpClient(httpParameters);
HttpGet httpGet;
HttpPost httpPost;
HttpDelete httpDelete;
HttpResponse httpResponse;
String authHeader;
if( authenticateData != null ) {
// -- Set basic authentication in header.
String base64EncodedCredentials = Base64.encodeToString(
(authenticateData.username + ":" + authenticateData.password).getBytes("US-ASCII"), Base64.URL_SAFE|Base64.NO_WRAP);
authHeader = "Basic " + base64EncodedCredentials;
} else {
authHeader = null;
}
// -- Send to server.
if( method == GET ) {
httpGet = new HttpGet(url);
if( authHeader != null ) {
httpGet.setHeader("Authorization", authHeader);
}
httpResponse = httpClient.execute(httpGet);
}
else if( method == POST) {
httpPost = new HttpPost(url);
if( authHeader != null ) {
httpPost.setHeader("Authorization", authHeader);
}
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
httpResponse = httpClient.execute(httpPost);
}
else if( method == DELETE) {
httpDelete = new HttpDelete(url);
httpDelete.setHeader("Content-Length", "0");
if( authHeader != null ) {
httpDelete.setHeader("Authorization", authHeader);
}
httpResponse = httpClient.execute(httpDelete);
}
else {
return null;
}
// -- Method 1 for obtaining response.
/*
InputStream is = httpResponse.getEntity().getContent();
// -- Convert response.
Scanner scanner = new Scanner(is);
// -- TODO: specify charset
String response = scanner.useDelimiter("\\A").next();
*/
// -- Method 2 for obtaining response.
String response = new BasicResponseHandler().handleResponse(httpResponse);
return response;
}
catch(SocketTimeoutException exception) {
exception.printStackTrace();
}
catch(ConnectTimeoutException exception) {
exception.printStackTrace();
}
catch(NoHttpResponseException exception) {
exception.printStackTrace();
}
catch(UnknownHostException exception) {
exception.printStackTrace();
}
catch(ClientProtocolException exception) {
exception.printStackTrace();
}
catch(IOException exception) {
exception.printStackTrace();
}
return null;
}
WebView代码 - 应该在包含WebView的活动中。
WebView webView = new WebView(Activity.this);
webView.loadDataWithBaseURL(url, response, "text/html", "utf-8", null);
答案 3 :(得分:0)
我建议不要实际调用setHttpAuthUsernamePassword()。
相反,每次只使用onReceivedHttpAuthRequest()来动态处理auth挑战。
这个,再加上
WebViewDatabase.getInstance(getContext()).clearHttpAuthUsernamePassword();
WebViewDatabase.getInstance(getContext()).clearUsernamePassword();
WebViewDatabase.getInstance(getContext()).clearFormData();
调用加载来清除遗留条目,我的问题就消失了。
答案 4 :(得分:0)
事实证明,这里有两个潜在的问题。
WebViewDatabase.clearHttpAuthUsernamePassword()
似乎无法在Android的某些设备/版本上正常工作,因为在清除数据库后调用WebView.getHttpAuthUsernamePassword()
仍会产生存储的密码。
可以通过自己实施这些方法解决这个问题。
第二个问题是,auth数据似乎也存储在内存中,这基本上是件好事,因为WebView
不必为每个后续HTTP请求查询数据库。但是,此缓存似乎在所有WebView之间共享,并且没有明显的方法可以清除它。事实证明,使用privateBrowsing = true
创建的WebView共享一个不同的缓存,其行为也略有不同:在最后一次私有浏览WebView被销毁之后,这个缓存似乎被彻底清除,下一个请求实际上是触发onReceivedHttpAuthRequest
。
下面是这两个解决方法的完整工作示例。如果你必须处理多个WebView,它可能会变得更复杂,因为你需要确保在重新创建它们之前将它们全部销毁。
public class HttpAuthTestActivity extends Activity {
ViewGroup webViewContainer;
Button logoutButton;
Button reloadButton;
WebView webView;
AuthStoreInterface authStore;
public interface AuthStoreInterface {
public void clear();
public void setHttpAuthUsernamePassword(String host, String realm, String username, String password);
public Pair<String, String> getHttpAuthUsernamePassword(String host, String realm);
}
//if you want to make the auth store persistent, you have implement a persistent version of this interface
public class MemoryAuthStore implements AuthStoreInterface {
Map<Pair<String, String>, Pair<String, String>> credentials;
public MemoryAuthStore() {
credentials = new HashMap<Pair<String, String>, Pair<String, String>>();
}
public void clear() {
credentials.clear();
}
public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) {
credentials.put(new Pair<String, String>(host, realm), new Pair<String, String>(username, password));
}
public Pair<String, String> getHttpAuthUsernamePassword(String host, String realm) {
return credentials.get(new Pair<String, String>(host, realm));
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
authStore = new MemoryAuthStore();
webViewContainer = (ViewGroup)findViewById(R.id.webview_container);
logoutButton = (Button)findViewById(R.id.logout_button);
reloadButton = (Button)findViewById(R.id.reload_button);
createWebView();
logoutButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
authStore.clear();
destroyWebView();
createWebView();
}
});
reloadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
webView.reload();
}
});
}
@Override
protected void onDestroy() {
webView.destroy();
super.onDestroy();
}
private void destroyWebView() {
webView.destroy();
webViewContainer.removeView(webView);
}
private void createWebView() {
//this is the important line: if you use this ctor with privateBrowsing: true, the internal auth cache will
//acutally be deleted in WebView.destroy, if there is no other privateBrowsing enabled WebView left only
webView = new WebView(this, null, android.R.attr.webViewStyle, true);
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) {
Pair<String, String> credentials = authStore.getHttpAuthUsernamePassword(host, realm);
if (credentials != null && handler.useHttpAuthUsernamePassword()) {
handler.proceed(credentials.first, credentials.second);
} else {
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View form = inflater.inflate(R.layout.http_auth_request, null);
new AlertDialog.Builder(HttpAuthTestActivity.this).setTitle(String.format("HttpAuthRequest (realm: %s, host %s)", realm, host))
.setView(form).setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
EditText usernameEdt = (EditText) form.findViewById(R.id.username);
EditText passwordEdt = (EditText) form.findViewById(R.id.password);
String u = usernameEdt.getText().toString();
String p = passwordEdt.getText().toString();
authStore.setHttpAuthUsernamePassword(host, realm, u, p);
handler.proceed(u, p);
}
}).setCancelable(true).setNegativeButton(android.R.string.cancel, new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
handler.cancel();
}
}).create().show();
}
}
});
webView.loadUrl("http://httpbin.org/basic-auth/test/test");
webViewContainer.addView(webView);
}
}
答案 5 :(得分:0)
我也遇到过这个问题。并找到了解决方案,希望这可以帮到你。
首先,方法onReceivedHttpAuthRequest()
仅在应用程序中被调用一次,除了使用cookie。
我写过这个方法:
public void syncCookie(Context context, String url) {
HttpClient httpClient = HttpClientContext.getInstance();
Cookie[] cookies = httpClient.getState().getCookies();
Cookie sessionCookie = null;
if (cookies.length > 0) {
sessionCookie = cookies[cookies.length - 1];
}
CookieManager cookieManager = CookieManager.getInstance();
if (sessionCookie != null) {
String cookieString = sessionCookie.getName() + "="
+ sessionCookie.getValue() + ";domain="
+ sessionCookie.getDomain();
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context);
cookieSyncManager.startSync();
cookieManager.setCookie(url, cookieString);
CookieSyncManager.getInstance().sync();
}
}
像这样使用:
WebView webView = ...;
webView.getSettings().setJavaScriptEnabled(true);
syncCookie(this,url);
webView.loadUri(url);
webView.setWebViewClient();
答案 6 :(得分:0)
我使用多进程来解决这个问题。
由于您的Activity / Fragment中的WebView需要处理Http Basic Authentication,因此将触发onRecievedHttpAuthRequest()。创建一个用户输入登录信息的对话框。
iex(1)> quote do
...(1)> fn
...(1)> unquote()
...(1)> _ -> true
...(1)> end
...(1)> end
** (SyntaxError) iex:2: expected clauses to be defined with -> inside: 'fn'
启动服务包含一个webview来处理http basic auth,并从上面的对话框中传递account and pwd get。
onRecievedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, String realm){
final Dialog dialog = new Dialog(context);
dialog.setContentView(R.layout.dialog_layout);
dialog.findViewById(R.id.confirmBtn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String account = ((EditText)dialog.findViewById(R.id.accountET)).getText().toString();
final String pwd = ((EditText)dialog.findViewById(R.id.pwdET)).getText().toString();
serviceIntent = new Intent(context, SSOAuthService.class);
serviceIntent.putExtra("url", authUrl);
serviceIntent.putExtra("account", account);
serviceIntent.putExtra("pwd", pwd);
context.startService(serviceIntent);
dialog.dismiss();
}
});
dialog.show();
}
作为AuthService中的完整http基本身份验证,请终止AuthService所处的进程。并且可以重置http基本身份验证。
答案 7 :(得分:0)
我还有另一种似乎很好用的解决方案。基本上,您使用WebView.loadUrl(url,AdditionalHeaders)加载URL并传递一个空白的Authorization标头。这似乎可以正确重置Webview。唯一的问题是,如果不重新加载原始URL,则会在循环中调用onReceivedHttpAuthRequest。因此,一旦获得onReceivedHttpAuthRequest,您将需要从用户处收集用户名/密码,然后重新加载原始URL,而无需传递空白的Authorization标头。
本质上,它的工作方式如下(未测试特定代码)。
Content