web-dev-qa-db-ja.com

SerilogおよびAsp.NetCoreを使用する場合は、ユーザーをログコンテキストに追加します

ASP.Net Core1.0プロジェクトと一緒にSerilogを使用しようとしています。現在ログインしているユーザーをプロパティに追加してログに記録できないようです。

誰かがこれをもう理解していますか?

私はこれを試しました:

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 = "[email protected]";
                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();
        ...
    }

しかし、これは常にユーザー名として「匿名」で終わります。

前もって感謝します

SørenRokkedal

13
Søren Rokkedal

ほんの数行のコードで、認証されたActiveDirectoryユーザーを取得することができました。私はコア認証、特にクレームについてはあまり経験がありませんが、おそらくこれはあなたを途中で連れて行くか、少なくともあなたと同様の問題を抱えているが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>
7

次のようなブロックで_nextを呼び出す必要があります。

public async Task Invoke(HttpContext httpContext)
{
    if (httpContext.User.Identity.IsAuthenticated)
    {
        var userFullName = (((ClaimsIdentity)httpContext.User.Identity).FindFirst(Member.FullnameClaimName).Value);
        var userName = "[email protected]";

        using (LogContext.PushProperty("Username", userName))
        using (LogContext.PushProperty("UserFullName", userFullName))
        {
            await _next(httpContext);
        }
    }
    else
    {
        await _next(httpContext);
    }
}
3

ミドルウェアはおそらく問題ありません。ただし、ミドルウェアを構成する順序は重要です。 EnrichLoggerミドルウェアは最初のものです。つまり、認証ミドルウェアの前に実行されます。 app.EnrichLogger呼び出しを、認証ミドルウェアを追加する場所のすぐ下に移動します(おそらくapp.UseAuthentication)。このようにして、EnrichLoggerミドルウェアの実行時にHttpContext.Userプロパティが適切に設定されます。

更新

実際、このミドルウェアを認証ミドルウェアの下に移動するだけでは不十分な場合があります。 IDはMVCミドルウェア内で(少なくとも一部の構成では)設定されているようです。これは、コントローラーアクションが実行されるまで(MVCミドルウェアの後でユーザーIDを下に移動することにより)、ミドルウェアからユーザーIDにアクセスできないことを意味します。ただし、これは遅すぎてログで使用できません。

代わりに、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>();
        });
        ...
    }
2
Matt Varblow