4

我有一个使用 Redis 分布式缓存和 cookie 身份验证的 .NET Core 3 项目(最近从 2.2 升级)。

它目前看起来像这样:

public void ConfigureServices(IServiceCollection services)
{
    // Set up Redis distributed cache
    services.AddStackExchangeRedisCache(...);

    ...

    services.ConfigureApplicationCookie(options =>
    {
        ...
        // Get a service provider to get the distributed cache set up above
        var cache = services.BuildServiceProvider().GetService<IDistributedCache>();

         options.SessionStore = new MyCustomStore(cache, ...);
    }):
}

问题是BuildServiceProvider()导致构建错误:

Startup.cs(...):警告 ASP0000:从应用程序代码中调用“BuildServiceProvider”会导致创建单例服务的额外副本。考虑替代方案,例如依赖注入服务作为“配置”的参数。

这似乎不是一个选项 -ConfigureApplicationCookie存在Startup.ConfigureServices并且只能配置新服务,Startup.Configure可以使用新服务,但不能覆盖CookieAuthenticationOptions.SessionStore成为我的自定义商店。

我之前尝试过添加services.AddSingleton<ITicketStore>(p => new MyCustomRedisStore(cache, ...))ConfigureApplicationCookie但这被忽略了。

显式设置CookieAuthenticationOptions.SessionStore似乎是让它使用本地内存存储以外的任何东西的唯一方法。

我在网上找到的每个BuildServiceProvider()示例都使用;

理想情况下,我想做类似的事情:

services.ConfigureApplicationCookieStore(provider => 
{
    var cache = provider.GetService<IDistributedCache>();
    return new MyCustomStore(cache, ...);
});

或者

public void Configure(IApplicationBuilder app, ... IDistributedCache cache)
{
    app.UseApplicationCookieStore(new MyCustomStore(cache, ...));
}

然后CookieAuthenticationOptions.SessionStore应该只使用我在那里配置的任何东西。

如何使应用程序 cookie 使用注入的存储?

4

2 回答 2

3

参考使用 DI 服务配置选项

如果您的自定义存储的所有依赖项都是可注入的,那么只需将您的存储和所需的依赖项注册到服务集合并使用 DI 服务来配置选项

public void ConfigureServices(IServiceCollection services) {
    // Set up Redis distributed cache
    services.AddStackExchangeRedisCache(...);

    //register my custom store
    services.AddSingleton<ITicketStore, MyCustomRedisStore>();

    //...

    //Use DI services to configure options
    services.AddOptions<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme)
        .Configure<ITicketStore>((options, store) => {
            options.SessionStore = store;
        });

    services.ConfigureApplicationCookie(options => {
        //do nothing
    }):
}

如果没有,那么解决实际注册的内容

例如

//Use DI services to configure options
services.AddOptions<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme)
    .Configure<IDistributedCache>((options, cache) => {
        options.SessionStore = new MyCustomRedisStore(cache, ...);
    });

笔记:

ConfigureApplicationCookie使用命名选项实例。-@柯克拉金

public static IServiceCollection ConfigureApplicationCookie(this IServiceCollection services, Action<CookieAuthenticationOptions> configure)
        => services.Configure(IdentityConstants.ApplicationScheme, configure);

该选项在将其添加到服务时需要包含名称。

于 2020-02-25T12:32:19.380 回答
1

为了在 .NET Core 3.0 中实现 Redis 票证,我们执行了以下操作,即上面的最终形式:

services.AddSingleton<ITicketStore, RedisTicketStore>();
services.AddOptions<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme)
     .Configure<ITicketStore>((options, store) => {
         options.SessionStore = store;
     });


services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
    .AddIdentityServerAuthentication(options =>
    {
           // ...configure identity server options
    }).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);

这是一个 Redis 实现:

public class RedisTicketStore : ITicketStore
{
    private const string KeyPrefix = "AuthSessionStore-";
    private IDistributedCache cache;

    public RedisTicketStore(IDistributedCache cache)
    {
        this.cache = cache;
    }

    public async Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        var guid = Guid.NewGuid();
        var key = KeyPrefix + guid.ToString();
        await RenewAsync(key, ticket);
        return key;
    }

    public Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        var options = new DistributedCacheEntryOptions();
        var expiresUtc = ticket.Properties.ExpiresUtc;
        if (expiresUtc.HasValue)
        {
            options.SetAbsoluteExpiration(expiresUtc.Value);
        }
        byte[] val = SerializeToBytes(ticket);
        cache.Set(key, val, options);
        return Task.FromResult(0);
    }

    public Task<AuthenticationTicket> RetrieveAsync(string key)
    {
        AuthenticationTicket ticket;
        byte[] bytes = null;
        bytes = cache.Get(key);
        ticket = DeserializeFromBytes(bytes);
        return Task.FromResult(ticket);
    }

    public Task RemoveAsync(string key)
    {
        cache.Remove(key);
        return Task.FromResult(0);
    }

    private static byte[] SerializeToBytes(AuthenticationTicket source)
    {
        return TicketSerializer.Default.Serialize(source);
    }

    private static AuthenticationTicket DeserializeFromBytes(byte[] source)
    {
        return source == null ? null : TicketSerializer.Default.Deserialize(source);
    }
}

Redis 实现来自:https ://mikerussellnz.github.io/.NET-Core-Auth-Ticket-Redis/

于 2020-12-14T17:58:08.230 回答