通过将源映射发布到 Azure Artifacts 符号服务器来安全地调试原始代码

若要在 DevTools 中安全地 查看和使用原始开发源代码,而不是 Web 服务器返回的编译、缩小和捆绑的生产代码,请使用 Azure Artifacts 符号服务器提供的源映射。

将源映射直接发布到 Web 服务器会使原始源代码公开可见。 若要避免使原始源代码公开可见,请将源映射发布到 Azure Artifacts 符号服务器。 使用此方法可以在调试生产网站时使用 DevTools 中的源映射,而无需将源映射发布到 Web 服务器。

源映射将编译的生产代码映射到原始开发源文件。 然后,在 DevTools 中,可以查看和使用熟悉的开发源文件,而不是已编译的代码。 若要详细了解源映射和在 DevTools 中使用源映射,请参阅 将已处理的代码映射到原始源代码进行调试

概念

必须在 Azure Artifacts 符号服务器上为源映射编制索引,以便在调试生产网站时,源映射可供 DevTools 使用。

为此,请在编译时将 x_microsoft_symbol_client_key 字符串属性添加到源映射。 此属性包含相应原始源文件的 256 位 SHA-2 哈希 的小写十六进制值。

然后,DevTools 能够计算每个已编译文件的此哈希,并使用哈希从 Azure Artifacts 符号服务器检索正确的源映射。 为了安全地检索源映射,DevTools 使用你提供的个人访问令牌连接到 Azure Artifacts 符号服务器。

步骤 1:为 Azure DevOps 生成个人访问令牌

将源映射到 Azure Artifacts 符号服务器需要 个人访问令牌 (或 PAT) 。 编译代码和发布源映射时,生成系统将使用此 PAT。

在 Azure DevOps 中生成 PAT:

  1. 转到 登录到 Azure DevOps 组织 https://dev.azure.com/{yourorganization}

  2. 在 Azure DevOps 中,转到 “用户设置>”“个人访问令牌”:

    Azure DevOps 中的“用户设置”菜单,其中包含“个人访问令牌”命令

    此时会显示 “个人访问令牌 ”页:

    Azure DevOps 中的“个人访问令牌”页

  3. 单击“ 新建令牌”。 此时会 打开“创建新的个人访问令牌 ”对话框:

    “创建新的个人访问令牌”对话框,其中选择了符号的“读取 & 写入”范围

  4. 在“ 名称 ”文本框中,输入 PAT 的名称,例如“发布源映射”。

  5. “过期” 部分中,输入 PAT 的到期日期。

  6. “范围 ”部分中,单击“ 显示所有范围” 以展开该部分。

  7. 向下滚动到“ 符号 ”部分,然后选中“ 读取 & 写入 ”复选框。

  8. 单击“创建”按钮。 此时将显示 “成功! ”对话框:

    包含要复制的 PAT 的“成功!”对话框

  9. 单击“ 复制到剪贴板 ”按钮以复制 PAT。 请确保复制令牌并将其存储在安全位置。 出于安全原因,不会再次显示它。

若要详细了解 PAT,请参阅 使用个人访问令牌

步骤 2:计算脚本的 SHA-256 哈希并将其追加到源映射

在应用程序生成过程的最后一步,对于要发布的每个源映射,应计算源映射对应的 JavaScript 文件的 SHA-256 哈希,并通过字符串属性将其追加到源映射 x_microsoft_symbol_client_key

生成系统因应用程序而异,因此没有明确的单一方法可以应用此功能。 但下面是一个示例 Webpack 5 插件,如果你正在使用它,则可以将其添加到 Webpack 配置:

// file: webpack.plugin-symbols.js
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the BSD 3-clause license.

const crypto = require('crypto');

module.exports = class PrepareSourceMapsForSymbolServerPlugin {
  /**
   * @param {import('webpack').Compiler} compiler
   * @returns {void}
   */
  apply(compiler) {
    compiler.hooks.emit.tap('PrepareSourceMapsForSymbolServerPlugin', (compilation) => {
      const files = Object.keys(compilation.assets);
      const sourceMaps = files.filter(v => v.endsWith('.map'));
      const sourceFilesAndMapsNames = sourceMaps.map(mapFileName => {
        const sourceFileName = mapFileName.substring(0, mapFileName.length - 4);
        return {
          sourceFileName,
          mapFileName,
        };
      });
      const sourceFiles = sourceFilesAndMapsNames.map(({ sourceFileName, mapFileName }) => {
        const sourceFile = compilation.assets[sourceFileName];
        const sourceFileBuffer = sourceFile.buffer();
        const hasher = crypto.createHash('sha256');
        hasher.write(sourceFileBuffer);
        const digest = hasher.digest();
        const sourceFileHash = digest.toString('hex');

        const sourceMapAsset = compilation.assets[mapFileName];
        const sourceMapSource = sourceMapAsset.source();
        const sourceMapContents = JSON.parse(sourceMapSource);
        sourceMapContents['x_microsoft_symbol_client_key'] = sourceFileHash;
        const rewrittenSourceMapContents = JSON.stringify(sourceMapContents);
        if (!sourceMapAsset.isBuffer()) {
          // Not a buffer -- write to the _value property
          sourceMapAsset._value = rewrittenSourceMapContents;
        } else {
          sourceMapAsset._valueAsBuffer = Buffer.from(rewrittenSourceMapContents, 'utf-8');
        }

        return {
          sourceFileName,
          mapFileName,
          sourceFileHash,
          sourceMapAsset,
        };
      });
    });
  }
};

然后,可以将插件添加到 plugins 配置文件中的 webpack.config.js 节:

const PrepareSourceMapsForSymbolServerPlugin = require('./webpack.plugin-symbols.js');

// ...

module.exports = (env, args) => {
  const mode = process.env.NODE_ENV || (env && env.NODE_ENV) || 'production';
  return {
    devtool: mode === 'production' ? 'hidden-source-map' : 'inline-source-map',
    resolve: {
      modules: [
        path.resolve('./node_modules'),
      ],
    },
    output: {
      publicPath: '/',
      filename: '[name].bundle.js',
      chunkFilename: '[name].chunk.js',
    },
    plugins: [
        // ... other plugins
        new PrepareSourceMapsForSymbolServerPlugin(),
    ]
  };
};

步骤 3:将源映射发布到 Azure Artifacts 符号服务器

完成以下任一选项即可发布源映射。

使用 Azure DevOps Pipelines 发布源映射

Azure DevOps 附带管道 PublishSymbols@2 生成任务。 此任务可用于将源映射发布到 Azure Artifacts 符号服务器。

请确保将 参数设置为 AllSourceMap来配置此任务indexableFileFormats

使用 发布源映射 symbol.exe

符号服务器团队发布可自动下载的 .NET Core 应用程序symbol.exe。 下载 symbol.exe后,可以运行命令将源映射发布到 Azure Artifacts 符号服务器:

symbol publish
        -d {root directory containing your source maps}
        -n {a unique name for this job}
        -s {service URL, such as https://artifacts.dev.azure.com/contoso}
        --patAuthEnvVar {name of environment variable containing a PAT}
        --indexableFileFormats SourceMap

请注意,此处的参数 -n 必须是唯一的。 将拒绝重复作业名称,甚至失败的作业名称。

另请参阅