如何在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的字段映射
- 通过Azure Monitor诊断设置,将Event Hub的数据流导入Application Insights。
- 在诊断设置的“日志”板块,选择将Event Hub消息发送到Application Insights。
- 由于日志为JSON格式,Application Insights会自动解析顶层字段到
customDimensions中,例如userName字段会直接出现在customDimensions.userName下,无需额外配置。
注意:若记录完整JWT令牌,需确保符合数据合规要求,避免存储敏感信息。
内容的提问来源于stack exchange,提问作者Rob
相关产品推荐
相关产品推荐

