Android WebView 添加authorization基本验证
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android WebView 添加authorization基本验证相关的知识,希望对你有一定的参考价值。
参考技术A 1. HTTP 401 错误 - 未授权: (Unauthorized)网页提示如下
您的Web服务器认为,客户端发送的 HTTP 数据流是正确的,但进入网址 (URL) 资源 , 需要用户身份验证 , 而相关信息 1 )尚未被提供, 或 2 )已提供但没有通过授权测试。这就是通常所知的“ HTTP 基本验证 ”。 需客户端提供的验证请求在 HTTP 协议中被定义为 WWW – 验证标头字段 (WWW-Authenticate header field)
桌面应用程序一般把 "用户名+冒号+密码"用BASE64编码的字符串放在http request 中的header Authorization中发送给服务端, 这种方式叫HTTP基本认证(Basic Authentication)。
在android端一般通过WebViewClient类的onReceivedHttpAuthRequest方法弹窗提示用户输入账号密码来实现
2. 原因:
因为request中没有包含Authorization header,服务器会返回一个401 Unauthozied给客户端,并且在Response的header“www-authentivate”中添加信息。当客户端把用户名密码用Base64加密后编码,放在Authorization header中发送给服务器,那么就会认证成功了。
3,解决:
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
Log.e("webview","onReceivedHttpAuthRequest");
final WebView mView = view;
final HttpAuthHandler mHandler = handler;
final EditText usernameInput =new EditText(WebviewActivity.this);
usernameInput.setHint("用户名");
final EditText passwordInput =new EditText(WebviewActivity.this);
passwordInput.setHint("密码");
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
LinearLayout ll =new LinearLayout(WebviewActivity.this);
ll.setOrientation(LinearLayout.VERTICAL);
ll.addView(usernameInput);
ll.addView(passwordInput);
AlertDialog.Builder authDialog =new AlertDialog
.Builder(WebviewActivity.this)
.setTitle("身份验证")
.setView(ll)
.setCancelable(false)
.setPositiveButton("确定", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int whichButton)
mHandler.proceed(usernameInput.getText().toString(), passwordInput.getText().toString());
dialog.dismiss();
)
.setNegativeButton("取消", new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int whichButton)
dialog.dismiss();
mView.stopLoading();
// onLoadListener.onAuthCancel((WebviewActivity)mView, mTitleTextView);
);
if(view!=null)
authDialog.show();
android webview在系统签名文件下报错解决
最近定制系统应用开发用到了系统签名,有个功能用到了WebView这个控件,
正常不添加系统签名情况下WebView是正常的,添加系统签名后Webview崩溃报错:
Binary XML file line #46: Binary XML file line #46: Error inflating class android.webkit.WebView
详细看了下错误信息
发现这一行:
For security reasons, WebView is not allowed in privileged processes 出于安全原因,特权进程中不允许使用WebView
换句话说就是因为安全性问题系统级应用不允许使用WebView这个控件;
那么是为什么不允许在系统级应用使用呢?那么问题就来了;java嘛,面向Google编程。出事先Google,90%以上的问题都有先辈碰到过,实在没有再去翻源码,查原因;
随即Google 百度一番,得知原因
WebView这个控件在首次运行时会先调用getProvider()方法检测uid,如果是系统进程或者root进程,就直接抛出异常 为什么会有这种安全机制呢?因为WebView允许运行js,如果用户通过js注入,那么js就可以肆无忌惮的使用系统权限, (系统级权限的APP不管要干嘛都可以无需做任何申请提示,可谓是为所欲为)这无疑是一个大漏洞,门户大开。
static WebViewFactoryProvider getProvider()
synchronized (sProviderLock)
// For now the main purpose of this function (and the factory abstraction) is to keep
// us honest and minimize usage of WebView internals when binding the proxy.
if (sProviderInstance != null) return sProviderInstance;
final int uid = android.os.Process.myUid();
if (uid == android.os.Process.ROOT_UID || uid == android.os.Process.SYSTEM_UID)
throw new UnsupportedOperationException(
"For security reasons, WebView is not allowed in privileged processes");
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getProvider()");
try
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.loadNativeLibrary()");
loadNativeLibrary();
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
Class<WebViewFactoryProvider> providerClass;
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getFactoryClass()");
try
providerClass = getFactoryClass();
catch (ClassNotFoundException e)
Log.e(LOGTAG, "error loading provider", e);
throw new AndroidRuntimeException(e);
finally
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "providerClass.newInstance()");
try
try
sProviderInstance = providerClass.getConstructor(WebViewDelegate.class)
.newInstance(new WebViewDelegate());
catch (Exception e)
sProviderInstance = providerClass.newInstance();
if (DEBUG) Log.v(LOGTAG, "Loaded provider: " + sProviderInstance);
return sProviderInstance;
catch (Exception e)
Log.e(LOGTAG, "error instantiating provider", e);
throw new AndroidRuntimeException(e);
finally
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
StrictMode.setThreadPolicy(oldPolicy);
finally
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
解决方法:
一:
如果是自编系统直接暴力改造系统framework层,直接干掉uid的判断;从根本上解决;
二:
从WebViewFactory这个类的GetProvider方法上看,在抛出异常之前有个判断,如果 sProviderInstance 等空就开始检测是不是在系统进程或者ROOT进程下运行
既然知道了有这一层判断,那么我们是否可以尝试通过模仿系统创建sProviderInstance一开始就手动构造个sProviderInstance 从而达到欺骗系统绕过这段检测呢?
看看系统是如何创建sProviderInstance的。这是getFactoryClass的源码
private static Class<WebViewFactoryProvider> getFactoryClass() throws ClassNotFoundException
Application initialApplication = AppGlobals.getInitialApplication();
try
// First fetch the package info so we can log the webview package version.
String packageName = getWebViewPackageName();
sPackageInfo = initialApplication.getPackageManager().getPackageInfo(packageName, 0);
Log.i(LOGTAG, "Loading " + packageName + " version " + sPackageInfo.versionName +
" (code " + sPackageInfo.versionCode + ")");
// Construct a package context to load the Java code into the current app.
Context webViewContext = initialApplication.createPackageContext(packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
initialApplication.getAssets().addAssetPath(
webViewContext.getApplicationInfo().sourceDir);
ClassLoader clazzLoader = webViewContext.getClassLoader();
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "Class.forName()");
try
return (Class<WebViewFactoryProvider>) Class.forName(CHROMIUM_WEBVIEW_FACTORY, true,
clazzLoader);
finally
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
catch (PackageManager.NameNotFoundException e)
// If the package doesn't exist, then try loading the null WebView instead.
// If that succeeds, then this is a device without WebView support; if it fails then
// swallow the failure, complain that the real WebView is missing and rethrow the
// original exception.
try
return (Class<WebViewFactoryProvider>) Class.forName(NULL_WEBVIEW_FACTORY);
catch (ClassNotFoundException e2)
// Ignore.
Log.e(LOGTAG, "Chromium WebView package does not exist", e);
throw new AndroidRuntimeException(e);
通过查看以上源代码可以得知其实系统也是通过反射来创建的,返回值是一个WebViewFactoryProvider的类,可以看到系统会首先加载CHROMIUM_WEBVIEW_FACTORY,也就是使用Chrome内核的WebView。这个方法是静态的,那么我们就可以模仿一下
public static void hookWebView()
int sdkInt = Build.VERSION.SDK_INT;
try
Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
Field field = factoryClass.getDeclaredField("sProviderInstance");
field.setAccessible(true);
Object sProviderInstance = field.get(null);
if (sProviderInstance != null)
log.debug("sProviderInstance isn't null");
return;
Method getProviderClassMethod;
if (sdkInt > 22)
getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
else if (sdkInt == 22)
getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
else
log.info("Don't need to Hook WebView");
return;
getProviderClassMethod.setAccessible(true);
Class<?> providerClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
Constructor<?> providerConstructor = providerClass.getConstructor(delegateClass);
if (providerConstructor != null)
providerConstructor.setAccessible(true);
Constructor<?> declaredConstructor = delegateClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
sProviderInstance = providerConstructor.newInstance(declaredConstructor.newInstance());
log.debug("sProviderInstance:", sProviderInstance);
field.set("sProviderInstance", sProviderInstance);
log.debug("Hook done!");
catch (Throwable e)
log.error(e);
在使用WebView这个控件前也就是onCreate()方法调用setContentView()布局前,调用一次hookWebView()方法,手动创建个sProviderInstance对象,从而绕过系统的检测;解决系统签名下WebView不允许使用的限制,
我用的定制系统是6.0的,后续的高版本源码是否有无被修改过,暂时不得而知,后续有时间再去翻翻高版本的源代码;
来源:Android Webview在系统签名下会崩溃报错的解决方案_阿贾克斯`的博客-CSDN博客
以上是关于Android WebView 添加authorization基本验证的主要内容,如果未能解决你的问题,请参考以下文章
如何将推送通知添加到WebView App(Android和iOS)
如何将推送通知添加到 WebView 应用程序(Android 和 iOS)
如何在我自己的 android 库模块中添加人行横道 webview?