【问题标题】:Add user to the log context when using Serilog and Asp.Net Core使用 Serilog 和 Asp.Net Core 时将用户添加到日志上下文
【发布时间】:2017-02-15 05:26:19
【问题描述】:

我正在尝试将 Serilog 与我的 ASP.Net Core 1.0 项目一起使用。我似乎无法将当前登录的用户添加到记录的属性中。

有人知道吗?

我试过这个:

using System.Threading.Tasks;
using Serilog.Context;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using xxx.Models;

namespace xxx.Utils
{
    public class EnrichSerilogContextMiddleware
    {
        private readonly RequestDelegate _next;
        public EnrichSerilogContextMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext httpContext)
        {

            var username = httpContext.User.Identity.Name;
            if (httpContext.User.Identity.IsAuthenticated)
            {
                var userFullName = (((ClaimsIdentity)httpContext.User.Identity).FindFirst(Member.FullnameClaimName).Value);
                var userName = "anyone@gmail.com";
                LoggerEnricher.AddEntryPointContext(userFullName, userName);
            }
            else
            {
                LoggerEnricher.AddEntryPointContext();
            }


            await _next(httpContext);
        }
    }

    public static class LoggerEnricher

    {
        public static void AddEntryPointContext(string userFullName = null, string username = null)
        {
            if (!string.IsNullOrWhiteSpace(username) || !string.IsNullOrWhiteSpace(userFullName))
            {
                LogContext.PushProperty("Username", username);
                LogContext.PushProperty("UserFullename", userFullName);
            }
            else
            {
                LogContext.PushProperty("Username", "Anonymous");
            }

        }

        public static void EnrichLogger(this IApplicationBuilder app)
        {
            app.UseMiddleware<EnrichSerilogContextMiddleware>();
        }
    }
}

我在 Startup.cs 中添加:

  public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
        loggerFactory.AddSerilog();
        app.EnrichLogger();
        ...
    }

但这总是以“匿名”作为用户名。

提前致谢

索伦·罗克达尔

【问题讨论】:

  • 您是否尝试在身份验证后将其添加到您的管道中?
  • 不确定你的意思
  • 我的意思是在调用app.UseAuthentication(....); 之后调用app.EnrichLogger();,或者无论你的身份验证方法是什么。在用户通过身份验证之前,您在管道中过早注入记录器,因此它始终是匿名的。
  • 我在调用之前有 app.UseIdentity() 来丰富记录器
  • @SørenRokkedal .. 感谢这是一篇旧帖子,但你有没有想过这个问题?谢谢

标签: asp.net-core serilog


【解决方案1】:

我只需几行代码就能够获得经过身份验证的 Active Directory 用户。我对核心身份验证(尤其是声明)不是很有经验,但也许这会让您顺利进行,或者至少可以帮助遇到与您类似问题但使用 AD 的其他人。

关键行是Enrich.FromLogContext()app.Use(async...

public class Startup
{
    public IConfigurationRoot Configuration { get; }

    public Startup(IHostingEnvironment env)
    {
        Log.Logger = new LoggerConfiguration()
                   .Enrich.FromLogContext() // Populates a 'User' property on every log entry
                   .WriteTo.MSSqlServer(Configuration.GetConnectionString("MyDatabase"), "Logs")
                   .CreateLogger();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.WithFilter(new FilterLoggerSettings
                                 {
                                     { "Default", LogLevel.Information },
                                     { "Microsoft", LogLevel.Warning },
                                     { "System", LogLevel.Warning }
                                 })
                     .AddSerilog();

        app.Use(async (httpContext, next) =>
                {
                    var userName = httpContext.User.Identity.IsAuthenticated ? httpContext.User.Identity.Name : "unknown";
                    LogContext.PushProperty("User", !String.IsNullOrWhiteSpace(userName) ? userName : "unknown");
                    await next.Invoke();
                });
    }
}

对于通过 IIS/Kestrel 进行的 AD 身份验证,web.config 需要一个 forwardWindowsAuthToken 设置,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <aspNetCore ... forwardWindowsAuthToken="true" />
  </system.webServer>
</configuration>

【讨论】:

    【解决方案2】:

    您的中间件可能没问题。但是配置中间件的顺序很重要。您的 EnrichLogger 中间件是第一个。这意味着它在身份验证中间件之前运行。将app.EnrichLogger 调用移动到您添加身份验证中间件的下方(可能是app.UseAuthentication)。这样,当您的 EnrichLogger 中间件运行时,HttpContext.User 属性将被正确设置。

    更新

    实际上,即使将这个中间件移到身份验证中间件之下也可能还不够。似乎可以在 MVC 中间件中设置身份(至少在某些配置中)。这意味着在执行控制器操作之前(通过在 MVC 中间件之后将其向下移动),您无法从中间件访问用户身份。但这为时已晚,无法在您的日志中使用。

    相反,您可能必须使用 MVC 过滤器将用户信息添加到日志上下文中。例如,您可以像这样创建一个过滤器:

    public class LogEnrichmentFilter : IActionFilter
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
    
        public LogEnrichmentFilter(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
            var httpContext = _httpContextAccessor.HttpContext;
            if (httpContext.User.Identity.IsAuthenticated)
            {
                LogContext.PushProperty("Username", httpContext.User.Identity.Name);
            }
            else
            {
                LogContext.PushProperty("Username", "Anonymous");
            }
        }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
        }
    }
    

    然后您可以使用 DI 全局应用您的过滤器。在您的 Services.cs 文件中:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddScoped<LogEnrichmentFilter>();
            services.AddMvc(o =>
            {
                o.Filters.Add<LogEnrichmentFilter>();
            });
            ...
        }
    

    【讨论】:

    • 您对中间件方法提出了一些有效的观点,但使用这种方法的用户名也并非随处可用 - 例如,它不会为Microsoft.AspNetCore.HostingMicrosoft.AspNetCore.Mvc.ViewFeaturesMicrosoft.AspNetCore.Routing.EndpointMiddleware 记录和许多其他人
    【解决方案3】:

    您需要像这样在块中调用_next

    public async Task Invoke(HttpContext httpContext)
    {
        if (httpContext.User.Identity.IsAuthenticated)
        {
            var userFullName = (((ClaimsIdentity)httpContext.User.Identity).FindFirst(Member.FullnameClaimName).Value);
            var userName = "anyone@gmail.com";
    
            using (LogContext.PushProperty("Username", userName))
            using (LogContext.PushProperty("UserFullName", userFullName))
            {
                await _next(httpContext);
            }
        }
        else
        {
            await _next(httpContext);
        }
    }
    

    【讨论】:

    • 感谢您的回复。我相信原始帖子包括 await _next(httpContext)。实施建议的解决方案并没有使其工作。每次调用 Invoke 方法时,httpContext.User.Identity.IsAuthenticated 为 false。有什么想法吗?
    • 感谢您的跟进。那时不确定这个。你的LoggerConfiguration 上设置了Enrich.FromLogContext() 吗?
    • 是的,我已经配置了这个
    猜你喜欢
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 2018-12-18
    • 1970-01-01
    相关资源
    最近更新 更多