
1. 项目概述为什么Android 9.0的HTTP配置成了“必看”如果你是从Android 9.0Pie开始接触开发或者你的应用需要兼容到Android 9.0及以上版本那么你大概率在调试网络请求时遇到过这个让人头疼的异常Cleartext HTTP traffic to xxx not permitted。这可不是你的代码写错了而是从Android 9.0开始谷歌引入了一项重大的安全策略变更——默认禁止应用进行明文HTTP通信。这个变化让很多还在使用HTTP接口进行开发、测试或者需要访问本地服务器、老旧内网服务的应用瞬间“哑火”。我刚开始接触这个限制时也花了不少时间去排查发现这背后不仅仅是加个配置那么简单它涉及到Android应用网络安全模型的根本性调整。简单来说Android 9.0及更高版本的系统默认将你的应用视为一个“默认安全”的实体。它假设你所有的网络通信都应该使用HTTPS即HTTP over TLS/SSL因为HTTPS能对传输的数据进行加密防止中间人窃听和篡改。任何试图使用未加密的HTTP协议发起的请求都会被系统网络栈直接拦截并抛出异常。这个设计的初衷无疑是好的强制开发者提升应用的安全性保护用户数据。但对于我们开发者而言在开发、测试阶段或者应用确实有访问非HTTPS资源的合理需求时这就成了一个必须跨越的“门槛”。因此掌握如何安全、正确地配置应用的网络安全性策略就成了Android 9.0开发者的必备技能。这不仅仅是让应用“能跑起来”更是在理解平台安全规范的前提下为自己的应用设计合理的网络访问边界。今天要详细拆解的network_security_config.xml文件就是实现这一目标的核心钥匙。它不是简单地“关闭”安全限制而是让你能够声明式地、精细化地控制你的应用可以与哪些服务器、以何种安全级别进行通信。2. 核心需求解析我们到底要解决什么问题在深入配置文件之前我们必须先厘清几个核心场景明白我们为什么要去动这个配置。盲目地允许所有HTTP访问是极其危险且不负责任的做法我们应该根据实际需求进行最小化、最精确的配置。2.1 开发与调试阶段的本地服务器访问这是最常见、也是最刚需的场景。在开发过程中我们的后端API服务很可能部署在本地的http://localhost:8080或局域网内的http://192.168.1.100:3000上。这些服务通常没有配置也没有必要配置HTTPS证书。在Android 9.0之前我们直接在代码里指定这个BaseUrl就能愉快地调试了。但现在如果不做任何配置应用一发起请求就会被系统阻止调试工作根本无法进行。需求本质我们需要让应用在调试版本debug build中能够信任特定的、非加密的本地或局域网地址。2.2 访问遗留系统或内部服务很多企业应用需要集成一些陈旧的内部系统或者访问某些硬件设备如打印机、内网NAS提供的HTTP管理界面。这些系统可能由于历史原因或硬件限制无法升级到HTTPS。如果你的应用必须与这些服务交互那么你就需要为这些特定的域名或IP地址“开绿灯”。需求本质在生产版本release build中允许应用访问指定的、已知安全的明文HTTP端点同时不影响应用其他部分对HTTPS的强制要求。2.3 处理用户自定义内容或代理有些应用比如浏览器、内嵌WebView的内容展示应用或者网络调试工具需要加载用户输入的或动态生成的URL。这些URL可能是HTTP的。虽然最佳实践是引导用户使用HTTPS但现实情况中完全禁止HTTP会损害功能完整性。需求本质在可控的、用户知情且同意例如在浏览器中访问非安全网站时的警告的前提下有选择地允许明文流量。2.4 理解“安全”与“便利”的平衡配置network_security_config的核心思想不是“关掉安全”而是“定义安全策略”。你需要明确告诉系统“我知道访问http://my-internal-server.local有风险但我评估后认为这个风险在可控范围内并且这是我的应用功能所必需的。” 这与直接在代码里捕获异常并忽略是截然不同的。后者是绕过系统安全机制而前者是与系统安全机制合作声明你的意图。3. network_security_config.xml 文件结构与详解这个配置文件是一个XML格式的文件它位于你的Android项目的res/xml/目录下。如果该目录不存在你需要手动创建。它的根元素是network-security-config所有策略都在这个标签内定义。3.1 基础结构概览一个最基础的、允许所有HTTP通信的配置强烈不推荐在生产环境使用看起来是这样的?xml version1.0 encodingutf-8? network-security-config base-config cleartextTrafficPermittedtrue trust-anchors certificates srcsystem/ /trust-anchors /base-config /network-security-config这个配置通过cleartextTrafficPermittedtrue全局允许了明文HTTP流量。这相当于把Android 9.0之前的行为又改了回来完全放弃了新安全策略带来的好处是极不安全的。我们接下来要做的就是把这个“大开关”分解成一个个精细的“小开关”。3.2 核心配置元素深度解析一个健壮、安全的配置通常会组合使用以下几个关键元素1.base-config基础配置这是应用的默认安全配置。如果你不指定domain-config所有网络请求都将遵循这里的规则。通常我们会在这里配置信任系统预装的CA证书srcsystem这是HTTPS验证的基础。2.domain-config域配置这是实现精细化控制的核心。你可以为特定的域名或一组域名定义独立的安全策略。一个应用可以包含多个domain-config块。3.domain域名声明在domain-config内部使用用于指定该策略适用的域名。它支持通配符例如*.example.com会匹配api.example.com和cdn.example.com但不匹配example.com本身需要单独声明。一个domain-config可以包含多个domain子标签。4.trust-anchors信任锚点定义应用信任哪些证书颁发机构CA。srcsystem表示信任Android系统内置的CA证书列表。你也可以通过srcraw/my_custom_ca来信任你自己添加的自定义CA证书例如企业内网证书。5.cleartextTrafficPermitted属性这个布尔属性是控制HTTP明文流量的关键。设置为true时允许向该配置所适用的域名发起HTTP请求设置为false默认时则禁止。3.3 一个实战配置示例假设我们有一个应用它需要访问生产环境HTTPS APIhttps://api.myapp.com在调试时访问本地HTTP开发服务器http://10.0.2.2:8080(Android模拟器访问本机)访问一个老旧的内网HTTP服务http://legacy.internal.mycompany.com我们的network_security_config.xml可以这样设计?xml version1.0 encodingutf-8? network-security-config !-- 1. 基础配置默认要求HTTPS信任系统CA -- base-config cleartextTrafficPermittedfalse trust-anchors certificates srcsystem/ /trust-anchors /base-config !-- 2. 调试域配置仅对debug构建变体生效 -- debug-overrides domain-config cleartextTrafficPermittedtrue domain includeSubdomainstruelocalhost/domain domain includeSubdomainstrue10.0.2.2/domain !-- 如果你的电脑在局域网IP是192.168.1.x也可以加进来 -- domain includeSubdomainstrue192.168.1.100/domain /domain-config /debug-overrides !-- 3. 特定生产域配置允许访问某个特定的内部HTTP服务 -- domain-config cleartextTrafficPermittedtrue domain includeSubdomainstruelegacy.internal.mycompany.com/domain /domain-config /network-security-config配置解读第1部分 (base-config)为应用设置了全局默认策略——禁止明文传输只信任系统CA。这保证了所有未在后续规则中特别声明的域名如api.myapp.com都必须使用有效的HTTPS。第2部分 (debug-overrides)这是一个特殊块其中定义的规则仅在应用以debug模式构建和安装时生效。这是最安全、最推荐的处理开发环境HTTP需求的方式。它明确声明只有在调试时才允许访问本地主机和特定模拟器地址的HTTP流量。当打Release包时这些规则自动失效。第3部分 (domain-config)专门为那个老旧的内网HTTP服务设置了一条规则允许其明文通信。这条规则在Debug和Release版本中都生效。3.4 在AndroidManifest.xml中启用配置创建好配置文件后你需要在AndroidManifest.xml文件的application标签中引用它application android:networkSecurityConfigxml/network_security_config ... ... /application这样系统在启动你的应用时就会读取并应用你定义的安全策略。4. 高级配置与安全最佳实践仅仅允许HTTP访问只是第一步。作为一个负责任的开发者我们还需要考虑更复杂的安全场景并遵循最佳实践。4.1 证书固定Certificate Pinning这是比单纯使用HTTPS更高级的安全手段。它要求你的应用只接受一个或一组特定的、预知的服务器证书或公钥哈希而不是系统信任的所有CA颁发的任何证书。这可以有效防御针对CA系统的攻击如CA被入侵以及设备上恶意安装的根证书。配置示例domain-config domain includeSubdomainstrueapi.mybank.com/domain pin-set expiration2024-12-31 !-- 这里填入服务器证书公钥的SHA-256哈希值 (Base64编码) -- pin digestSHA-2567HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y/pin !-- 可以配置一个备份Pin用于证书轮换 -- pin digestSHA-256fwza0LRMXouZHRC8Ei4PyuldPDcf3UKgO/04cDM1oE/pin /pin-set trust-anchors certificates srcsystem/ /trust-anchors /domain-config注意事项证书过期expiration属性必须设置并且需要定期更新。否则过期后连接会失败。备份Pin务必设置至少一个备份Pin用于在服务器证书到期轮换时平滑过渡。否则新证书部署后所有老版本应用将无法连接。获取Pin值可以使用OpenSSL命令或在线工具从服务器的证书中提取公钥哈希。生产环境务必直接从可信渠道获取。适用范围证书固定是一把双刃剑增加了安全性的同时也降低了灵活性。通常只用于对安全要求极高的核心API域名。4.2 信任用户自定义证书在某些企业场景中设备可能安装了企业自签名的CA证书用于解密和审查内部流量。如果你的应用需要在这种环境下工作可以选择信任用户添加的证书。base-config cleartextTrafficPermittedfalse trust-anchors certificates srcsystem/ certificates srcuser / !-- 信任用户安装的CA证书 -- /trust-anchors /base-config警告srcuser会信任设备上所有用户自行安装的CA证书这可能会降低安全性因为恶意应用可能诱导用户安装恶意根证书。仅在明确知晓风险且有必要如企业MDM管理设备时才使用。4.3 针对WebView的单独配置从Android 8.0API 26开始你可以通过android:networkSecurityConfig为单个WebView实例设置独立的安全配置。但更常见的做法是应用级的network-security-config会同样影响WebView中加载的内容。这意味着如果你允许了某个域名的HTTP访问那么WebView加载该域名的HTTP页面也将正常工作。5. 常见问题排查与实战技巧即使配置看起来正确在实际开发中你还是会遇到各种“坑”。下面是我总结的一些常见问题和解决方法。5.1 配置不生效的排查步骤检查文件位置和名称确保文件是res/xml/network_security_config.xml名字一个字母都不能错。检查Manifest引用确认AndroidManifest.xml中android:networkSecurityConfig的值指向正确没有拼写错误。清理并重建项目Android Studio的构建缓存有时会出问题。执行Build - Clean Project然后Build - Rebuild Project。卸载重装应用设备上旧版本应用的配置可能被缓存。务必卸载后重新安装。检查构建变体如果你使用了debug-overrides请确保你正在运行的是Debug构建变体而不是“Profile”或“Release”。在Android Studio的运行按钮旁可以下拉选择。查看Logcat日志搜索关键字 “Cleartext” 或 “NetworkSecurityConfig”。系统在拦截请求或解析配置出错时通常会输出相关日志。5.2 模拟器与真机访问本地服务的特殊处理Android模拟器模拟器将10.0.2.2这个特殊IP映射到宿主机的localhost。所以你的后端服务运行在宿主机localhost:8080在模拟器应用中就应该访问http://10.0.2.2:8080。务必在配置中包含这个域名。USB连接的真机手机和电脑需要在同一局域网Wi-Fi。在电脑上运行ipconfig(Windows) 或ifconfig(macOS/Linux) 查看电脑的局域网IP如192.168.1.100。然后在手机应用中访问http://192.168.1.100:8080并在配置文件中允许该IP。使用域名代替IP在开发中可以修改电脑的hosts文件将一个域名如dev.myapp.local指向127.0.0.1。然后在Android配置中允许dev.myapp.local的HTTP访问。这样做的好处是代码中的BaseUrl可以更接近生产环境使用域名只需切换域名对应的IP即可。5.3 第三方库的网络请求问题如果你的应用使用了OkHttp、Retrofit、Volley等网络库或者像Glide、Picasso这样的图片加载库它们发起的网络请求同样受network_security_config的约束。OkHttp/Retrofit它们直接使用系统的网络栈所以安全配置自动生效。WebView如前所述受应用级配置影响。原生代码NDK通过libcurl或其他库发起的网络请求同样受此限制。Android的系统级安全策略作用于整个应用进程。5.4 从低版本升级到Android 9.0的兼容性考虑如果你的老应用之前没有这个配置在升级targetSdkVersion到 28Android 9.0或更高时为了保持现有HTTP接口的可用性你可能会考虑暂时全局允许明文流量作为过渡。过渡方案不推荐长期使用base-config cleartextTrafficPermittedtrue trust-anchors certificates srcsystem/ /trust-anchors /base-config正确的迁移路径首先使用上述过渡配置确保应用在新系统上不崩溃。立即与后端团队协作制定将HTTP接口升级为HTTPS的计划。在代码中逐步将API的BaseUrl从http://替换为https://。使用domain-config精细地将仍需要使用HTTP的域名如内网服务、第三方未升级的服务单独列出并允许。最终目标是将base-config中的cleartextTrafficPermitted改回false并移除所有不必要的HTTP域名配置实现全站HTTPS。5.5 关于“Cleartext”的精确含义这里有一个容易混淆的点cleartext明文不仅仅指HTTP。它指的是任何未加密的传输层协议。这包括HTTPWebSocket (ws://)FTPSMTP, IMAP, POP3 (未使用STARTTLS或SSL/TLS时)以及任何你自定义的使用TCP/UDP且未加密的协议所以如果你的应用使用了ws://的WebSocket连接同样需要在此配置文件中允许对应的域名。6. 在持续集成/持续交付CI/CD中的配置管理在团队协作和自动化构建中管理不同环境开发、测试、生产的网络安全配置是一个挑战。你肯定不希望将调试用的本地IP地址配置打到生产包中。推荐策略使用资源文件变体Build Flavors Source Sets这是最清晰、最Android原生支持的方式。在build.gradle中定义风味android { flavorDimensions environment productFlavors { dev { dimension environment // 可以在这里定义不同的应用ID后缀方便同时安装 applicationIdSuffix .dev } prod { dimension environment } } }创建对应的源集目录src/dev/res/xml/network_security_config.xml(开发环境配置)src/prod/res/xml/network_security_config.xml(生产环境配置)编写不同环境的配置开发配置 (src/dev/...)可以包含debug-overrides和允许内部测试服务器HTTP访问的domain-config。生产配置 (src/prod/...)应该非常严格base-config禁止明文只包含必要的证书固定或极少数必须的HTTP域名配置。构建当你在Android Studio中选择devDebug变体进行构建时Gradle会自动合并src/dev/下的配置。构建prodRelease包时则使用src/prod/下的配置。这样就从根源上隔离了环境配置安全又省心。7. 安全红线与最后提醒在灵活配置的同时有几条安全红线绝对不能碰永远不要在生产版本中使用base-config cleartextTrafficPermittedtrue。这是将你的应用和用户数据完全暴露在风险之中。最小化原则在domain-config中只添加你必须使用的HTTP域名。能用HTTPS的绝对不要配置为允许HTTP。善用debug-overrides所有仅为调试而生的HTTP规则务必放在debug-overrides块内。这是系统为你提供的完美安全隔离机制。定期审计配置随着应用迭代有些内网服务可能下线或升级为HTTPS。定期检查你的network_security_config.xml文件移除不再需要的HTTP域名规则。将HTTPS迁移作为高优先级任务允许HTTP永远是临时方案。必须制定明确计划推动所有依赖方将服务升级到HTTPS并最终移除所有HTTP配置。理解并正确使用network_security_config.xml标志着你从“让应用跑起来”的开发者向“构建安全、可靠应用”的工程师迈进了一步。它强迫你思考应用的网络边界和安全模型这是一个非常好的实践。刚开始可能会觉得多了一道麻烦的步骤但习惯之后你会发现它带来的清晰性和安全性能让你的应用在复杂的网络环境中更加稳健。下次遇到Cleartext traffic not permitted时希望你能从容地打开这个配置文件精准地解决问题而不是简单地全局关闭安全限制。