如何在 Nancy 中注入 ClaimsPrincipal

How to inject ClaimsPrincipal in Nancy

我有一个 Nancy 模块和一个装饰器 class,它们需要知道 ClaimsPrincipal 才能检索用户的电子邮件地址。所以我将构造函数声明为

public EmailDecorator(IDbConnectionFactory connectionFactory,
                      IPrincipal principal,
                      IEmailClient emailClient) : base(connectionFactory)

我正在努力弄清楚的是如何将委托人注入构造函数。我正在使用 Simple Injector for DI,它非常有效。但是,如果我重写 ConfigureRequestContainer() 方法(它作为 NancyContext 的参数)来实例化 IPrincipal,我会得到一个异常

protected override void ConfigureRequestContainer(
    TinyIoCContainer container, NancyContext context)
{
    container.Register<IPrincipal>((c, o) => context.CurrentUser); 
    base.ConfigureRequestContainer(container, context);
}

异常表明 Simple Injector 不知道在默认 TinyIOC 容器中注册的 IPrincipal

The configuration is invalid. Creating the instance for type InvoiceModule failed. The constructor of type EmailDecorator contains the parameter with name 'principal' and type IPrincipal that is not registered. Please ensure IPrincipal is registered, or change the constructor of EmailDecorator.

编辑

我花了太长时间试图解决这个问题,我怀疑答案是停止尝试这样做。在我原来的示例中,我试图将 IPrincipal 注入到不正确的装饰器构造函数中。相反,我需要注入某种形式的服务,允许我在调用装饰器中的方法之一时派生 IPrincipal,例如Func<IPrincipal>。 Nancy 提供了一个可覆盖的方法 ConfigureRequestContainer(),每个请求都会调用该方法,因此可能会被使用:

protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
    container.Register<Func<IPrincipal>>((c, o) =>
    {
        return () => context.CurrentUser;
    });
}

除了请求容器是 TinyIoCContainer 而我使用的是 SimpleInjector。所以,也许我可以添加一个 SimpleInjector 注册,将解析卸载到请求容器?

_container.RegisterSingleton<Func<IPrincipal>>(() => nancy.Resolve<Func<IPrincipal>>());

其实不是,nancy和请求容器不是同一个容器。所以也许某个地方有一个容器可以成功解析 IPrincipal 但我不知道如何访问它。练习的 objective 是为了避免必须修改 repo 代码的方法签名以包含 ClaimsPrincipal,有点像过去可以调用 Thread.CurrentPrincipal 的日子。仍在寻找线索,但将开始修改方法签名以包含 ClaimsPrincipal。

答案是使用IHttpContextAccessor。在Startup.cs的ConfigureServices()方法中添加一个新的服务定义:

public void ConfigureServices(IServiceCollection services)
{           
    services.AddSingleton<IAppSettings>(settings);
    services.AddSingleton<IHttpContextAccessor>(new HttpContextAccessor());
    …
 }

然后将app.ApplicationServices作为参数传递给引导程序:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAppSettings settings)
{
    var log = ConfigureLogger(settings);
    app.UseOwin(buildFunc => 
    { 
        buildFunc.UseMonitoringAndLogging(log, HealthCheckAsync);
        buildFunc.UseNancy(cfg => 
            cfg.Bootstrapper = new Bootstrapper(env, log, app.ApplicationServices));
    });
}

在Bootstrapper class ApplicationStartup()方法中,在Simple Injector容器中注册服务:

    public Bootstrapper(IHostingEnvironment env, ILogger log, IServiceProvider services)
    {
        _env = env;
        _log = log;
        _services = services;
    }

    …

    _container.Register<IHttpContextAccessor>(() =>
        (IHttpContextAccessor) _services.GetService(typeof(IHttpContextAccessor))); 

然后在装饰器class中,在构造函数中添加IHttpContextAccessor

public EmailDecorator(IDbConnectionFactory connectionFactory,
                      ILogger log,
                      IHttpContextAccessor httpContextAccessor,
                      IEmailClient emailClient) : base(connectionFactory, log)
{
    _emailClient = emailClient;
    _httpContextAccessor = httpContextAccessor;
}

可以从 _httpContextAccessor.HttpContext 属性 访问 ClaimsPrincipal