如何将 network_security_config.xml 添加到 expo 应用程序中的清单而不弹出

Ant*_*e S 11 android android-manifest react-native expo

我正在用 expo 构建一个应用程序。
使用axios来处理请求。
该 API 具有自签名证书。
请求在 android 7 上失败(它们在 android 9 上工作)

我在网上读到我需要添加一个network_security_config.xml到 android 清单。关联。

我怎样才能在expo(可能是app.json)中做到这一点而不弹出?

谢谢 !

sil*_*fer 20

我面临着类似的问题(需要使用自签名证书连接到本地 API),经过大量的研究和实验,我终于找到了解决方案。您需要创建一个配置插件,这需要 Expo SDK 版本 41+。请注意,您将失去使用 Expo Go 的能力,但您将保留在托管工作流程中(即无需更改本机代码),并且您可以使用 EAS 构建来构建自定义开发客户端,这基本上是 Expo Go 的一个版本根据您的项目量身定制。

将证书添加到设备的用户证书列表中:

(如果您在下面的网络配置中包含(原始)证书,则可能不需要此步骤,请参阅此链接。)转到Settings -> Security -> Advanced -> Encryption & credentials -> Install a certificate导入证书

确保证书确实是问题所在:

try {
    const response = await axios.post(urlPath, payload);
} catch (error) {
    console.error(error.request?._response);
}
Run Code Online (Sandbox Code Playgroud)

error.request._response如果您收到网络错误并读取,您就会知道您的应用不信任该证书java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

创建插件:

现在,您将创建一个配置插件,它基本上是在 Expoprebuild阶段运行的 JS 函数,用于在构建本机项目之前修改本机配置,例如 Android 清单。

在项目根目录中,创建一个plugins包含以下两个文件的文件夹:

  • network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>
Run Code Online (Sandbox Code Playgroud)
  • trust-local-certs.js:
const {AndroidConfig, withAndroidManifest } = require('@expo/config-plugins');
const {Paths} = require('@expo/config-plugins/build/android');
const path = require('path');
const fs = require('fs');
const fsPromises = fs.promises;

const { getMainApplicationOrThrow} = AndroidConfig.Manifest

const withTrustLocalCerts = config => {
    return withAndroidManifest(config, async config => {
        config.modResults = await setCustomConfigAsync(config, config.modResults);
        return config;
    });
}

async function setCustomConfigAsync(
    config,
    androidManifest
) {

    const src_file_pat = path.join(__dirname, "network_security_config.xml");
    const res_file_path = path.join(await Paths.getResourceFolderAsync(config.modRequest.projectRoot),
        "xml", "network_security_config.xml");

    const res_dir = path.resolve(res_file_path, "..");

    if (!fs.existsSync(res_dir)) {
        await fsPromises.mkdir(res_dir);
    }

    try {
        await fsPromises.copyFile(src_file_pat, res_file_path);
    } catch (e) {
        throw e;
    }

    const mainApplication = getMainApplicationOrThrow(androidManifest);
    mainApplication.$["android:networkSecurityConfig"] = "@xml/network_security_config";

    return androidManifest;
}

module.exports = withTrustLocalCerts;
Run Code Online (Sandbox Code Playgroud)

运行expo prebuild并链接插件

为了使用该插件,您必须app.json在项目根目录中有一个名为的文件。我不能 100% 确定我从哪里获取该文件,但我相信它是在我第一次运行时自动创建的expo prebuild。笔记:

  • 我建议将 Expo SDK 升级到最新版本(当前为 44),因为现在命令prebuild(显然与 相同eject)现在是完全可逆的(例如,它不会安装以前安装的大部分附加依赖项)。
  • 预构建将创建本机文件夹,但是一旦完成插件设置,您就可以安全地删除这些文件夹(如果您想保留在托管工作流程中,您确实应该删除它们!当您运行 EAS 构建时,EAS 将假设如果看到本机文件夹,则为裸工作流程,并且可能不会再次运行您的插件)。
  1. 运行expo prebuild并按照提示操作 -> 这应该创建app.json文件。
  2. 删除生成的本机android文件夹(以及ios,如果也创建了)。
  3. 在 中app.json,将以下内容添加到密钥末尾expo
    "plugins": [
      "./plugins/trust-local-certs.js"
    ]   
    
    Run Code Online (Sandbox Code Playgroud)
  4. 运行expo prebuild --no-install并检查其中android/app/src/main/AndroidManifest.xml包含对您的网络配置的引用,并且该引用android/app/src/main/res/xml/network_security_config.xml已从您的plugins目录中正确复制。
  5. 如果一切顺利,您的插件设置正确,您应该再次删除本机文件夹(请参阅上面的注释)。

设置并运行 EAS 构建:

如果您还没有这样做,请按照以下说明设置您的 EAS 构建项目(安装 EAS CLI,eas build:configure在 中运行并配置开发配置文件eas.json)。然后,运行eas build --profile development --platform android。这将在云中创建一个自定义开发客户端(在此阶段运行插件prebuild),您可以将其安装在您的设备上并充当 Expo Go 的替代品。要启动 Metro 服务器,请运行expo start --dev-client. 然后,您的开发客户端应该能够建立与 Metro 服务器的连接,并且如果您再次运行 axios 请求,它应该会通过:)

  • 感谢您提供出色的解决方案!我最终将“network_security_config.xml”内联为模板文字,并使用“writeFile()”将其写入资源目录。我还使用了“expo-module-scripts”并创建了一个单独的插件存储库,这是使用 TS 插件的最简单方法。 (2认同)