使用Serilog和Asp.Net Core时,将用户添加到日志上下文中

Sør*_*dal 13 serilog asp.net-core

我正在尝试将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>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我通过添加以下命令在Startup.cs中触发:

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

但这最终会以"匿名"作为用户名.

提前致谢

SørenRokkedal

Kev*_*ski 7

我只需几行代码即可获得经过身份验证的Active Directory用户.我对Core身份验证并不是很有经验,特别是声称,但是这可能会让你在途中或者至少帮助那些带有类似问题但与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();
                });
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <aspNetCore ... forwardWindowsAuthToken="true" />
  </system.webServer>
</configuration>
Run Code Online (Sandbox Code Playgroud)


Mat*_*low 5

你的中间件可能没问题。但是配置中间件的顺序很重要。您的 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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


Nic*_*rdt 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);
    }
}
Run Code Online (Sandbox Code Playgroud)