在没有 BuildServiceProvider() 的情况下为 ConfigureApplicationCookie 设置自定义 SessionStore
Set a custom SessionStore for ConfigureApplicationCookie without BuildServiceProvider()
我有一个使用 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(...): warning ASP0000: Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.
这似乎不是一个选项 - ConfigureApplicationCookie
在 Startup.ConfigureServices
中并且只能配置新服务,Startup.Configure
可以使用新服务,但不能覆盖CookieAuthenticationOptions.SessionStore
成为我的定制商店。
我尝试在 ConfigureApplicationCookie
之前添加 services.AddSingleton<ITicketStore>(p => new MyCustomRedisStore(cache, ...))
,但它被忽略了。
明确设置 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 使用注入存储?
引用Use DI services to configure options
如果您的自定义商店的所有依赖项都是可注入的,那么只需将您的商店和所需的依赖项注册到服务集合并使用 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
uses a named options instance. - @KirkLarkin
public static IServiceCollection ConfigureApplicationCookie(this IServiceCollection services, Action<CookieAuthenticationOptions> configure)
=> services.Configure(IdentityConstants.ApplicationScheme, configure);
该选项在添加到服务时需要包含名称。
为了在 .NET Core 3.0 中实现 Redis Tickets,我们执行了以下操作,即上面的最终形式:
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/
我有一个使用 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(...): warning ASP0000: Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.
这似乎不是一个选项 - ConfigureApplicationCookie
在 Startup.ConfigureServices
中并且只能配置新服务,Startup.Configure
可以使用新服务,但不能覆盖CookieAuthenticationOptions.SessionStore
成为我的定制商店。
我尝试在 ConfigureApplicationCookie
之前添加 services.AddSingleton<ITicketStore>(p => new MyCustomRedisStore(cache, ...))
,但它被忽略了。
明确设置 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 使用注入存储?
引用Use DI services to configure options
如果您的自定义商店的所有依赖项都是可注入的,那么只需将您的商店和所需的依赖项注册到服务集合并使用 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
uses a named options instance. - @KirkLarkin
public static IServiceCollection ConfigureApplicationCookie(this IServiceCollection services, Action<CookieAuthenticationOptions> configure)
=> services.Configure(IdentityConstants.ApplicationScheme, configure);
该选项在添加到服务时需要包含名称。
为了在 .NET Core 3.0 中实现 Redis Tickets,我们执行了以下操作,即上面的最终形式:
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/