我有一个asp.net核心应用程序,我正在配置cookie身份验证和OpenID Connect身份验证。我也在使用Session和分布式SQL服务器缓存。
在cookie身份验证配置中,我将SessionStore属性设置为使用我的分布式缓存故障单存储。我将会话超时设置为60分钟。当我直接将项目放入会话时,它使用sql缓存,条目显示60分钟的滑动过期时间。一切正常。现在,当cookie auth使用分布式缓存票据存储时,我也可以看到数据库中的条目(滑动60分钟超时)。但是,如果我让网页停留20分钟或更长时间然后刷新页面,则故障单存储会删除数据库中用于cookie的缓存条目;即使整整60分钟还没有过去。在调试故障单存储时,我看到调用Retrieve get调用,然后调用Remove,然后再调用Retrieve;在那时,没有更多的缓存条目。
我确定我在某个地方缺少某些设置,但是只是过早地清理和删除了cookie缓存条目。我无法弄清楚原因。
以下是我创业公司的相关部分:
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
// app insights
services.AddApplicationInsightsTelemetry(this.Configuration);
// authentication
services.AddAuthentication(options => options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
// options
services.AddOptions()
.Configure<ConfigurationOptions>(this.Configuration)
.AddUtilitiesLayerConfigurationOptions(this.Configuration)
.AddDataLayerConfigurationOptions(this.Configuration)
.AddServicesLayerConfigurationOptions(this.Configuration)
.AddWebAppLayerConfigurationOptions(this.Configuration);
// caching/session
services.AddDistributedSqlServerCache(this.ConfigureSqlServerCacheOptions);
services.AddSession(this.ConfigureSessionOptions);
// mvc
services.AddMvc(ConfigureMvcOptions);
// custom services
services
.AddConfigurationLayerServices()
.AddCommonLayerServices()
.AddUtilitiesLayerServices()
.AddServicesLayerServices(this.Configuration);
}
public void Configure(
IApplicationBuilder app,
IHostingEnvironment env,
IServiceProvider serviceProvider,
ILoggerFactory loggerFactory,
ITicketStore distributedCacheTicketStore)
{
// setup logging
loggerFactory.AddDebug();
// app insights request telemetry (this must be first)
app.UseApplicationInsightsRequestTelemetry();
// exceptions
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage(new DeveloperExceptionPageOptions { SourceCodeLineCount = 10 });
}
else
{
app.UseExceptionHandler("/error");
}
// app insights exception telemetry (right after exception config)
app.UseApplicationInsightsExceptionTelemetry();
// session
app.UseSession();
// status code pages (redirect to error controller)
app.UseStatusCodePagesWithRedirects("/error/{0}");
// static files
// this is before auth, so no static files require auth
// if we wanth auth for static files, move this after the auth middleware
app.UseStaticFiles();
// auth
app.UseCookieAuthentication(this.BuildCookieAuthenticationOptions(distributedCacheTicketStore));
app.UseOpenIdConnectAuthentication(this.BuildOpenIdConnectOptions());
// mvc
app.UseMvc();
}
private CookieAuthenticationOptions BuildCookieAuthenticationOptions(ITicketStore ticketStore)
{
var configuration = new ConfigurationOptions();
this.Configuration.Bind(configuration);
return new CookieAuthenticationOptions
{
CookieSecure = CookieSecurePolicy.SameAsRequest,
CookieName = configuration.Session.AuthenticationCookieName,
AccessDeniedPath = "/access-denied",
SessionStore = ticketStore
};
}
private OpenIdConnectOptions BuildOpenIdConnectOptions()
{
var configuration = new ConfigurationOptions();
this.Configuration.Bind(configuration);
return new OpenIdConnectOptions
{
ClientId = configuration.AzureActiveDirectory.ClientID,
Authority = configuration.AzureActiveDirectory.Authority,
PostLogoutRedirectUri = configuration.AzureActiveDirectory.PostLogoutRedirectUri,
ResponseType = OpenIdConnectResponseType.CodeIdToken,
Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = this.OnRedirectToIdentityProvider,
OnRemoteFailure = this.OnRemoteFailure,
OnTokenValidated = this.OnTokenValidated,
OnAuthorizationCodeReceived = this.OnAuthorizationCodeReceived,
OnAuthenticationFailed = this.OnAuthenticationFailed
}
};
}
}
这是我的DistributedCacheTicketStore:
public class DistributedCacheTicketStore : ITicketStore
{
private readonly DistributedCacheTicketStoreOptions options;
private readonly IDistributedCache distributedCache;
private readonly IDataProtector dataProtector;
private readonly ILogger<DistributedCacheTicketStore> logger;
public DistributedCacheTicketStore(
IOptions<DistributedCacheTicketStoreOptions> optionsAccessor,
IDistributedCache distributedCache,
IDataProtectionProvider dataProtectionProvider,
ILogger<DistributedCacheTicketStore> logger)
{
this.options = optionsAccessor.Value;
this.distributedCache = distributedCache;
this.dataProtector = dataProtectionProvider.CreateProtector(this.GetType().FullName);
this.logger = logger;
}
public async Task<string> StoreAsync(AuthenticationTicket ticket)
{
var key = Guid.NewGuid().ToString();
var ticketBytes = this.dataProtector.Protect(TicketSerializer.Default.Serialize(ticket));
await this.distributedCache.SetAsync(key, ticketBytes, new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(this.options.Session.TimeoutMinutes) });
this.logger.AuthenticationTicketStoredInCache(key);
return key;
}
public async Task RenewAsync(string key, AuthenticationTicket ticket)
{
var ticketBytes = this.dataProtector.Protect(TicketSerializer.Default.Serialize(ticket));
await this.distributedCache.SetAsync(key, ticketBytes, new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(this.options.Session.TimeoutMinutes) });
this.logger.AuthenticationTicketRenewedInCache(key);
}
public async Task<AuthenticationTicket> RetrieveAsync(string key)
{
var ticketBytes = await this.distributedCache.GetAsync(key);
var ticket = TicketSerializer.Default.Deserialize(this.dataProtector.Unprotect(ticketBytes));
this.logger.AuthenticationTicketRetrievedFromCache(key);
return ticket;
}
public async Task RemoveAsync(string key)
{
var ticketBytes = await this.distributedCache.GetStringAsync(key);
if (ticketBytes != null)
{
await this.distributedCache.RemoveAsync(key);
this.logger.AuthenticationTicketRemovedFromCache(key);
}
}
}