You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Azure APIM中通过Event Hub将JWT信息写入Application Insights自定义维度

问题描述

我正在使用Azure APIM并配置了JWT验证,尝试记录JWT令牌(理想情况下仅提取用户名)但未成功。请问如何通过Event Hub传输后将JWT令牌(或用户名)记录到Application Insights的customDimension中?

现有APIM策略

<policies>
    <inbound>
        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" output-token-variable-name="jwt-token">
            <openid-config url="https://OUR_IDP/.well-known/openid-configuration" />
        </validate-jwt>
        <set-header name="caller-objectid" exists-action="override">
            <value>@(((Jwt)context.Variables["jwt-token"]).Subject)</value>
        </set-header>
        <set-variable name="message-id" value="@(Guid.NewGuid())" />
        <!--context.Request.Headers.GetValueOrDefault("Authorization", "DEFAULT"),-->
        <log-to-eventhub logger-id="LOGGER_ID_HERE" partition-id="0">@{ 
          var requestLine = string.Format("{0} {1} HTTP/1.1\n", 
                                                        context.Request.Method, 
                                                        context.Request.Url.Path + context.Request.Url.QueryString);

          var body = context.Request.Body?.As<string>(true);
          if (body != null && body.Length > 1024)
          {
              body = body.Substring(0, 1024);
          }

          var headers = context.Request.Headers
                               .Where(h => h.Key != "Ocp-Apim-Subscription-Key")
                               .Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
                               .ToArray<string>();

          var headerString = (headers.Any()) ? string.Join("\n", headers) + "\n" : string.Empty;

          return "request:"   + context.Variables["message-id"] + "\n"
                              + requestLine + headerString + "\n" + body;
      }</log-to-eventhub>
    </inbound>
    <backend>
        <forward-request follow-redirects="true" />
    </backend>
    <outbound>
        <log-to-eventhub logger-id="LOGGER_ID_HERE" partition-id="0">@{ 
          var statusLine = string.Format("HTTP/1.1 {0} {1}\n", 
                                              context.Response.StatusCode, 
                                              context.Response.StatusReason);

          var body = context.Response.Body?.As<string>(true);
          if (body != null && body.Length > 1024)
          {
              body = body.Substring(0, 1024);
          }

          var headers = context.Response.Headers
                                          .Select(h => string.Format("{0}: {1}", h.Key, String.Join(", ", h.Value)))
                                          .ToArray<string>();

          var headerString = (headers.Any()) ? string.Join("\n", headers) + "\n" : string.Empty;

          return "response:"  + context.Variables["message-id"] + "\n"
                              + statusLine + headerString + "\n" + body;
     }</log-to-eventhub>
    </outbound>
    <on-error />
</policies>
解决方案

步骤1:提取JWT中的用户名(或令牌)

validate-jwt策略已将解析后的JWT存入jwt-token变量,可从JWT的Claims中提取用户名。常见用户名Claim类型包括name、preferred_username、email,需根据你的IDP配置选择对应类型:

// 提取用户名示例(以preferred_username为例)
var userName = ((Jwt)context.Variables["jwt-token"]).Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value ?? "UnknownUser";

// 若需记录完整JWT令牌(注意:令牌含敏感信息,需谨慎存储)
var fullToken = context.Request.Headers.GetValueOrDefault("Authorization", "").Replace("Bearer ", "");

步骤2:修改log-to-eventhub策略,输出结构化JSON日志

原策略输出纯文本日志,不利于后续解析映射到customDimension。建议改为JSON格式,将用户名(或令牌)作为独立字段加入:

修改后的入站日志策略示例:

<log-to-eventhub logger-id="LOGGER_ID_HERE" partition-id="0">@{ 
  var requestLine = string.Format("{0} {1} HTTP/1.1", 
                                                        context.Request.Method, 
                                                        context.Request.Url.Path + context.Request.Url.QueryString);

  var body = context.Request.Body?.As<string>(true);
  if (body != null && body.Length > 1024)
  {
      body = body.Substring(0, 1024);
  }

  var headers = context.Request.Headers
                       .Where(h => h.Key != "Ocp-Apim-Subscription-Key")
                       .ToDictionary(h => h.Key, h => String.Join(", ", h.Value));

  // 提取用户名
  var userName = ((Jwt)context.Variables["jwt-token"]).Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value ?? "UnknownUser";

  var logObject = new {
      messageType = "request",
      messageId = context.Variables["message-id"],
      userName = userName,
      // 可选:若需记录完整令牌,取消下方注释
      // jwtToken = context.Request.Headers.GetValueOrDefault("Authorization", "").Replace("Bearer ", ""),
      requestLine = requestLine,
      headers = headers,
      body = body
  };

  return Newtonsoft.Json.JsonConvert.SerializeObject(logObject);
}</log-to-eventhub>

修改后的出站日志策略示例:

<log-to-eventhub logger-id="LOGGER_ID_HERE" partition-id="0">@{ 
  var statusLine = string.Format("HTTP/1.1 {0} {1}", 
                                              context.Response.StatusCode, 
                                              context.Response.StatusReason);

  var body = context.Response.Body?.As<string>(true);
  if (body != null && body.Length > 1024)
  {
      body = body.Substring(0, 1024);
  }

  var headers = context.Response.Headers
                                  .ToDictionary(h => h.Key, h => String.Join(", ", h.Value));

  // 复用入站时提取的用户名(可提前存入变量避免重复解析)
  var userName = ((Jwt)context.Variables["jwt-token"]).Claims.FirstOrDefault(c => c.Type == "preferred_username")?.Value ?? "UnknownUser";

  var logObject = new {
      messageType = "response",
      messageId = context.Variables["message-id"],
      userName = userName,
      statusLine = statusLine,
      headers = headers,
      body = body
  };

  return Newtonsoft.Json.JsonConvert.SerializeObject(logObject);
}</log-to-eventhub>

步骤3:配置Event Hub到Application Insights的字段映射

  1. 通过Azure Monitor诊断设置,将Event Hub的数据流导入Application Insights。
  2. 在诊断设置的“日志”板块,选择将Event Hub消息发送到Application Insights。
  3. 由于日志为JSON格式,Application Insights会自动解析顶层字段到customDimensions中,例如userName字段会直接出现在customDimensions.userName下,无需额外配置。

注意:若记录完整JWT令牌,需确保符合数据合规要求,避免存储敏感信息。


内容的提问来源于stack exchange,提问作者Rob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 08:55:25