ASP.NET Core 网站在 30 分钟后超时

Posted

技术标签:

【中文标题】ASP.NET Core 网站在 30 分钟后超时【英文标题】:ASP.NET Core website timing out after 30 minutes 【发布时间】:2017-02-01 14:51:12 【问题描述】:

我有一个托管在 Azure 网站上的 ASP.NET Core MVC 应用程序,我在其中实现了会话和身份。我的问题是,30 分钟后,我退出了。我在过去 30 分钟内是否活跃并不重要。

做了一些搜索,我发现问题是 SecurityStamp 的东西,found here。我已经尝试通过执行以下操作来实现这一点:

这是我使用安全标记的 UserManager 实现:

public class UserManager : UserManager<Login>

    public UserManager(
        IUserStore<Login> store,
        IOptions<IdentityOptions> optionsAccessor,
        IPasswordHasher<Login> passwordHasher,
        IEnumerable<IUserValidator<Login>> userValidators,
        IEnumerable<IPasswordValidator<Login>> passwordValidators,
        ILookupNormalizer keyNormalizer,
        IdentityErrorDescriber errors,
        IServiceProvider services,
        ILogger<UserManager<Login>> logger)
        : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    
        // noop
    

    public override bool SupportsUserSecurityStamp => true;

    public override async Task<string> GetSecurityStampAsync(Login login)
    
        return await Task.FromResult("MyToken");
    

    public override async Task<IdentityResult> UpdateSecurityStampAsync(Login login)
    
        return await Task.FromResult(IdentityResult.Success);
    

这是我在 Startup.cs 上的 ConfigureServices 方法:

public void ConfigureServices(IServiceCollection services)

    // Add framework services.
    services.AddApplicationInsightsTelemetry(Configuration);

    services.AddSingleton(_ => Configuration);

    services.AddSingleton<IUserStore<Login>, UserStore>();
    services.AddSingleton<IRoleStore<Role>, RoleStore>();

    services.AddIdentity<Login, Role>(o =>
    
        o.Password.RequireDigit = false;
        o.Password.RequireLowercase = false;
        o.Password.RequireUppercase = false;
        o.Password.RequiredLength = 6;
        o.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromDays(365);
        o.Cookies.ApplicationCookie.SlidingExpiration = true;
        o.Cookies.ApplicationCookie.AutomaticAuthenticate = true;
    )
        .AddUserStore<UserStore>()
        .AddUserManager<UserManager>()
        .AddRoleStore<RoleStore>()
        .AddRoleManager<RoleManager>()
        .AddDefaultTokenProviders();

    services.AddScoped<SignInManager<Login>, SignInManager<Login>>();
    services.AddScoped<UserManager<Login>, UserManager<Login>>();

    services.Configure<AuthorizationOptions>(options =>
    
        options.AddPolicy("Admin", policy => policy.Requirements.Add(new AdminRoleRequirement(new RoleRepo(Configuration))));
        options.AddPolicy("SuperUser", policy => policy.Requirements.Add(new SuperUserRoleRequirement(new RoleRepo(Configuration))));
        options.AddPolicy("DataIntegrity", policy => policy.Requirements.Add(new DataIntegrityRoleRequirement(new RoleRepo(Configuration))));
    );

    services.Configure<FormOptions>(x => x.ValueCountLimit = 4096);
    services.AddScoped<IPasswordHasher<Login>, PasswordHasher>();

    services.AddDistributedMemoryCache();
    services.AddSession();

    services.AddMvc();

    // repos
    InjectRepos(services);

    // services
    InjectServices(services);

最后,这是我在 Startup.cs 上的 Configure 方法:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseApplicationInsightsRequestTelemetry();

    if (env.IsDevelopment())
    
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    
    else
    
        app.UseExceptionHandler("/home/error");
    

    app.UseStatusCodePages();

    app.UseStaticFiles();

    app.UseSession();
    app.UseIdentity();

    app.UseMiddleware(typeof (ErrorHandlingMiddleware));
    app.UseMiddleware(typeof (RequestLogMiddleware));

    app.UseMvc(routes =>
    
        routes.MapRoute(
            name: "default",
            template: "controller=Home/action=Index/id?");
    );

我在这里的实现有什么问题?

更新:等一下……我注意到我的 UserManager 没有从任何接口继承安全标记的东西,这是需要的吗?

【问题讨论】:

【参考方案1】:

这仅仅是因为您需要enable and configure Data Protection。 cookie 和会话设置看起来正确。现在对您来说,每当应用程序被回收或服务器负载平衡到另一台服务器或发生新的部署等时,它都会在内存中创建一个新的数据保护密钥,因此您的用户的会话密钥无效。因此,您需要做的就是将以下内容添加到 Startup.cs:

 services.AddDataProtection()
       .PersistKeysToFileSystem(new DirectoryInfo(@"D:\writable\temp\directory\"))
       .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

使用文档了解如何正确设置它以及保存数据保护密钥的位置(文件系统、redis、注册表等)的不同选项。您可以将数据保护密钥视为 asp.net 中 web.config 的机器密钥的替换。

由于您提到您使用的是 Azure,您可以使用此包 Microsoft.AspNetCore.DataProtection.AzureStorage 来保存密钥,以便它持续存在。所以你可以use this example of how to use Azure Storage。

【讨论】:

所以我的应用程序池每 30 分钟回收一次,并且与我上次登录的时间同步?这对我来说没有意义。 它可能不会经常被回收,不。但是 AspNetCore 文档提到数据保护密钥是默认在内存中生成的——并且是保护会话和身份 cookie 数据(以及 ValidateAntiForgeryToken 操作过滤器)的内容,如果此数据保护密钥对用户无效变化。 我刚刚注意到 Azure 上的 the default settings for Data Protection 实际上非常擅长设置您。您可以简单地添加代码services.AddDataProtection();,您应该可以启动并运行。 Azure 将自动为您保留密钥。此外,对于使用 IIS 的任何人,他们也可以在单台机器上正常工作,而无需 PersistKeysToFileSystem 选项,因为默认设置很好地设置了这一点,如文档中所示。 我尝试将那一行代码直接添加到我的 ConfigureServices 方法中这一行之后:services.AddSingleton(_ =&gt; Configuration);,但它仍然不起作用。【参考方案2】:

您是否托管在 IIS 下?如果是这样,您的代码可能没有任何问题,但您的应用程序池可能会被回收(检查应用程序池的高级设置)。当这种情况发生时,你的二进制文件是否会从内存中卸载并被一个新的替换,它的 PID 会发生变化?

【讨论】:

抱歉,这是托管在 Azure 上的,所以我认为我无法控制 IIS。

以上是关于ASP.NET Core 网站在 30 分钟后超时的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 会话超时

如何在 ASP.NET Core 的 web.config 中设置会话超时

小5聊Asp.Net Core 2.1基础之部署在IIS上接口请求超时解决方法

“Identity.External”的 ASP.NET Core 标识异常

ASP.NET 窗体身份验证超时和会话超时

ASP.NET Core:调用之间的空闲超时有延迟