由于摘要完整性问题,加载 appsettings.Production.json 时出错

Error loading appsettings.Production.json due to digest integrity issue

我正在开发启用了 PWA 的 Blazor WebAssembly 应用程序,并使用文件 appsettings.jsonappsettings.Development.jsonappsettings.Production.json。最后一个是空的,因为它包含在生产环境部署到 kubernetes 集群时要替换的秘密。

我正在使用 k8s 进行部署,并使用 Secret 资源将空 appsettings.Production.json 文件替换为加密文件,并将其放入基于 nginx 的容器中,其中包含已发布的 blazor 应用程序。

现在我在浏览器中遇到了这个问题:

当使用 docker 在 CI 管道中构建应用程序时,该文件是一个空的 json 文件,并且计算出的 SHA 与计算出的一个不匹配通过构建过程。

我的问题是:我如何在部署期间替换 appsettings.Production.json,比构建过程晚得多,并且完整性测试不会失败文件?

文件 blazor.boot.json 不包含 appsetting.Production.json 文件的任何 SHA:

{
  "cacheBootResources": true,
  "config": [
    "appsettings.Development.json",
    "appsettings.json",
    "appsettings.Production.json"
  ],
  "debugBuild": false,
  "entryAssembly": "IrisTenantWeb",
  "icuDataMode": 0,
  "linkerEnabled": true,
  "resources": {
    "assembly": {
      "Azure.Core.dll": "sha256-rzNx\/GlDpiutVRPzugT82owXvTopmiixMar68xLA6L8=",
      // Bunch of .dlls,
      "System.Private.CoreLib.dll": "sha256-S7l+o9J9ivjCunMa+Ms\/JO\/kVaXLW8KTAjq1eRjY4EA="
    },
    "lazyAssembly": null,
    "pdb": null,
    "runtime": {
      "dotnet.timezones.blat": "sha256-SQvzbzBfueaAxSKIKE1khBH02NH2MJJaWDBav\/S5MSs=",
      "dotnet.wasm": "sha256-YXYNlLeMqRPFVpY2KSDhleLkNk35d9KvzzwwKAoiftc=",
      "icudt.dat": "sha256-m7NyeXyxM+CL04jr9ui1Z6pVfMWwhHusuz5qNZWpAwA=",
      "icudt_CJK.dat": "sha256-91bygK5voY9lG5wxP0\/uj7uH5xljF9u7iWnSldT1Z\/g=",
      "icudt_EFIGS.dat": "sha256-DPfeOLph83b2rdx40cKxIBcfVZ8abTWAFq+RBQMxGw0=",
      "icudt_no_CJK.dat": "sha256-oM7Z6aN9jHmCYqDMCBwFgFAYAGgsH1jLC\/Z6DYeVmmk=",
      "dotnet.5.0.5.js": "sha256-Dvb7uXD3+JPPqlsw2duS+FFNQDkFaxhIbSQWSnhODkM="
    },
    "satelliteResources": null
  }
}

但是 service-worker-assets.js 文件确实包含为其计算的 SHA:

self.assetsManifest = {
  "assets": [
    {
      "hash": "sha256-EaNzjsIaBdpWGRyu2Elt6mv3X+48iD9gGaSN8xAm3ao=",
      "url": "appsettings.Development.json"
    },
    {
      "hash": "sha256-RIn54+RUdIs1IeshTgpWlNViz\/PZ\/1EctFaVPI9TTAA=",
      "url": "appsettings.json"
    },
    {
      "hash": "sha256-RIn54+RUdIs1IeshTgpWlNViz\/PZ\/1EctFaVPI9TTAA=",
      "url": "appsettings.Production.json"
    },
    {
      "hash": "sha256-OV+CP+ILUqNY7e7\/MGw1L5+Gi7EKCXEYNJVyBjbn44M=",
      "url": "css\/app.css"
    },
   // ...
  ],
  "version": "j39cUu6V"
};

NOTE: You can see that both appsettings.json and appsettings.Production.json have the same hash because they are both the empty json file {}. But in production the second one is having a computed hash of YM2gjmV5... and issuing the error.

我不能针对不同的环境使用不同的构建过程,因为那样无法确保在暂存和生产中使用相同的构建。我需要使用相同的 docker 图像,但在部署时替换文件。

我编辑了wwwroot/service-worker.published.js文件,第一行如下:

// Caution! Be sure you understand the caveats before publishing an application with
// offline support. See https://aka.ms/blazor-offline-considerations

self.importScripts('./service-worker-assets.js');
self.addEventListener('install', event => event.waitUntil(onInstall(event)));
self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
self.addEventListener('fetch', event => event.respondWith(onFetch(event)));

const cacheNamePrefix = 'offline-cache-';
const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
const offlineAssetsExclude = [ /^service-worker\.js$/ ];

async function onInstall(event) {
    console.info('Service worker: Install');

    // Fetch and cache all matching items from the assets manifest
    const assetsRequests = self.assetsManifest.assets
        .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
        .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
        .map(asset => new Request(asset.url, { integrity: asset.hash }));
    await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
}
...

我添加了一组模式,类似于 offlineAssetsIncludeofflineAssetsExclude 以指示我想跳过完整性检查的文件。

...
const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
const offlineAssetsExclude = [ /^service-worker\.js$/ ];
const integrityExclude = [ /^appsettings\.Production\.json$/ ]; // <-- new variable

然后在 onInstall,而不是总是返回带有 integrity 设置的 Request,我跳过它以排除模式:

...
async function onInstall(event) {
    console.info('Service worker: Install');

    // Fetch and cache all matching items from the assets manifest
    const assetsRequests = self.assetsManifest.assets
        .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
        .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
        .map(asset => {
            // Start of new code
            const integrity =
                integrityExclude.some(pattern => pattern.test(asset.url))
                ? null
                : asset.hash;

            return !!integrity 
              ? new Request(asset.url, { integrity }) 
              : new Request(asset.url);
            // End of new code
        });
    await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
}
...

我将等待其他人发表评论并提出其他解决方案,因为理想的响应是为文件设置正确的 SHA 哈希值,而不是忽略它。