背景
自 Android 9.0 起,默认禁止使用 HTTP 进行访问。当尝试使用 HTTP 链接时,将会收到以下错误信息:
"Cleartext HTTP traffic to " + host + " not permitted"
为了解决这一问题,下面介绍两种破解方法:
XML布局设置
在 Android 9.0 及以上版本,需要通过以下配置允许 HTTP 访问。在 android/app/res
目录下新建 network_security_config.xml
文件,内容如下:
<network-security-config>
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
</network-security-config>
然后在 android/app
目录下的 AndroidManifest.xml
文件中的 application
标签内声明文件:
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
其实只需在 AndroidManifest.xml
文件中的 application
标签内声明 android:usesCleartextTraffic="true"
就可以了。如果还有特殊的配置,则需要配置 networkSecurityConfig
文件。另外需要说明的是,networkSecurityConfig
文件中的 cleartextTrafficPermitted
属性会优先于 application
标签内的 usesCleartextTraffic
,这意味着,即使在 application
标签中设置了 android:usesCleartextTraffic="false"
,但在 networkSecurityConfig
文件中设置了 cleartextTrafficPermitted="true"
,仍然会开启明文传输。
代码设置
为了验证结果,首先需要在 AndroidManifest.xml
文件中的 application
标签内声明文件:
android:usesCleartextTraffic="false"
接下来编写反射工具类,用于调用对象的指定方法:
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 反射工具类,用于调用对象的指定方法
*/
public class ReflectionUtil {
private static final String TAG = "ReflectionUtil"; // 日志标签
/**
* 调用对象的指定方法
*
* @param owner 方法所属的对象实例
* @param methodName 方法名
* @param b 方法参数,boolean类型
* @return 方法的返回值,如果调用失败则返回null
*/
public static Object invokeMethod(Object owner, String methodName, boolean b) {
if (owner == null) {
Log.e(TAG, methodName + " not invoked, owner is null"); // 记录错误日志:对象为空无法调用方法
return null;
}
try {
Class<?> ownerClass = owner.getClass(); // 获取对象的类
Method method = ownerClass.getDeclaredMethod(methodName, boolean.class); // 获取指定方法
method.setAccessible(true); // 设置方法为可访问
return method.invoke(owner, b); // 调用方法
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, methodName +
" not invoked, InvocationTargetException or NoSuchFieldException or IllegalAccessException: " +
e.getMessage()); // 记录错误日志:方法调用失败
}
return null;
}
}
最后,通过以下代码设置:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean permittedOld = NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(); // 获取旧的 cleartext 流量是否允许
Log.i("TAG", "onCreate, permittedOld: " + permittedOld); // 记录旧的 cleartext 流量是否允许的日志
// 动态设置 setCleartextTrafficPermitted 方法
ReflectionUtil.invokeMethod(NetworkSecurityPolicy.getInstance(), "setCleartextTrafficPermitted", true);
boolean permittedNew = NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(); // 获取新的 cleartext 流量是否允许
Log.i("TAG", "onCreate, permittedNew: " + permittedNew); // 记录新的 cleartext 流量是否允许的日志
}
简而言之,通过 XML 布局和反射设置,可以绕过 Android 9.0 的 HTTP 访问限制。