UseCookieAuthentication with Distributed Cache Ticket Store在设置timetout之前删除缓存条目

时间:2016-11-10 14:43:30

标签: asp.net asp.net-mvc session caching asp.net-core

我有一个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);
        }
    }
}

0 个答案:

没有答案