Dela via


Sessions- och tillståndshantering i ASP.NET Core

Av Rick Anderson, Kirk Larkin och Diana LaRose

HTTP är ett tillståndslöst protokoll. Som standard är HTTP-begäranden oberoende meddelanden som inte behåller användarvärden. I den här artikeln beskrivs flera metoder för att bevara användardata mellan begäranden.

Vägledning Blazor för tillståndshantering, som lägger till eller ersätter vägledningen i den här artikeln, finns i ASP.NET KärntillståndshanteringBlazor.

Statushantering

Tillstånd kan lagras med hjälp av flera metoder. Varje metod beskrivs senare i den här artikeln.

Metod för lagring Mekanism för förvaring
Cookies HTTP-cookies. Kan innehålla data som lagras med hjälp av appkod på serversidan.
Sessions tillstånd HTTP-cookies och appkod på serversidan
Temporär data (TempData) HTTP-cookies eller sessionstillstånd
Frågesträngar HTTP-frågesträngar
Dolda fält Fält för HTTP-formulär
HttpContext.Items (på engelska) Appkod på serversidan
Cache Appkod på serversidan

SignalR/Blazor Server och kontextbaserad tillståndshantering i HTTP

SignalR appar bör inte använda sessionstillstånd och andra tillståndshanteringsmetoder som förlitar sig på en stabil HTTP-kontext för att lagra information. SignalRAppar kan lagra tillstånd per anslutning iContext.Items hubben. Mer information och alternativa tillståndshanteringsmetoder för Blazor Server appar finns i ASP.NET KärntillståndshanteringBlazor.

Webbkakor

Cookies lagrar data över begäranden. Eftersom cookies skickas med varje begäran bör deras storlek hållas till ett minimum. Helst bör endast en identifierare lagras i en cookie med de data som lagras av appen. De flesta webbläsare begränsar cookie storleken till 4096 byte. Endast ett begränsat antal cookies är tillgängliga för varje domän.

Eftersom cookies kan manipuleras måste de valideras av appen. Cookies kan raderas av användare och upphöra att gälla på klienter. Cookies är dock i allmänhet den mest varaktiga formen av datapersistens på klienten.

Cookies används ofta för personalisering, där innehållet anpassas för en känd användare. Användaren identifieras och autentiseras i de flesta fall inte. De cookie kan lagra användarens namn, kontonamn eller unikt användar-ID, till exempel ett GUID. De cookie kan användas för att komma åt användarens personliga inställningar, till exempel deras föredragna bakgrundsfärg på webbplatsen.

Se EU:s allmänna dataskyddsförordning (GDPR) när du utfärdar cookies och hanterar integritetsfrågor. Mer information finns i Stöd för den allmänna dataskyddsförordningen (GDPR) i ASP.NET Core.

Sessions tillstånd

Sessionstillstånd är ett ASP.NET kärnscenario för lagring av användardata medan användaren bläddrar i en webbapp. Sessionstillstånd använder ett arkiv som underhålls av appen för att bevara data mellan begäranden från en klient. Sessionsdata backas upp av ett cacheminne och betraktas som tillfälliga data. Webbplatsen bör fortsätta att fungera utan sessionsdata. Kritiska programdata bör lagras i användardatabasen och cachelagras i sessionen endast som en prestandaoptimering.

Sessionen stöds inte i SignalR appar eftersom en SignalR hubb kan köras oberoende av en HTTP-kontext. Detta kan till exempel inträffa när en lång avsökningsbegäran hålls öppen av en hubb utöver livslängden för begärans HTTP-kontext.

ASP.NET Core upprätthåller sessionstillståndet genom att tillhandahålla en till klienten cookie som innehåller ett sessions-ID. Sessions-ID cookie :

  • Skickas till appen med varje begäran.
  • Används av appen för att hämta sessionsdata.

Sessionstillståndet uppvisar följande beteenden:

  • Sessionen cookie är specifik för webbläsaren. Sessioner delas inte mellan webbläsare.
  • Sessionscookies raderas när webbläsarsessionen avslutas.
  • Om en cookie tas emot för en session som har upphört att gälla skapas en ny session som använder samma session cookie.
  • Tomma sessioner behålls inte. Sessionen måste ha minst ett värde inställt för att bevara sessionen mellan begäranden. När en session inte behålls genereras ett nytt sessions-ID för varje ny begäran.
  • Appen behåller en session under en begränsad tid efter den senaste begäran. Appen ställer antingen in tidsgränsen för sessionen eller använder standardvärdet 20 minuter. Sessionstillstånd är idealiskt för att lagra användardata:
    • Det är specifikt för en viss session.
    • Där data inte kräver permanent lagring mellan sessioner.
  • Sessionsdata tas bort antingen när implementeringen ISession.Clear anropas eller när sessionen upphör att gälla.
  • Det finns ingen standardmekanism för att informera appkoden om att en klientwebbläsare har stängts eller när sessionen cookie tas bort eller har upphört att gälla på klienten.
  • Sessionstillståndscookies markeras inte som nödvändiga som standard. Sessionstillståndet fungerar inte om inte spårning tillåts av webbplatsbesökaren. Mer information finns i Stöd för den allmänna dataskyddsförordningen (GDPR) i ASP.NET Core.
  • Det finns ingen ersättning för den cookiefria sessionsfunktionen från ASP.NET Framework eftersom den anses vara osäker och kan leda till sessionsfixeringsattacker.

Varning

Lagra inte känsliga data i sessionstillstånd. Användaren kanske inte stänger webbläsaren och rensar sessionen cookie. Vissa webbläsare har giltiga sessionscookies i alla webbläsarfönster. En session kanske inte är begränsad till en enda användare. Nästa användare kan fortsätta att bläddra i appen med samma session cookie.

Providern för minnesintern cache lagrar sessionsdata i minnet på den server där appen finns. I ett servergruppsscenario:

Konfigurera sessionstillstånd

Mellanprogram för att hantera sessionstillstånd ingår i ramverket. Om du vill aktivera mellanprogram för sessionen Program.cs måste du innehålla:

Följande kod visar hur du ställer in den minnesinterna sessionsprovidern med en standardimplementering i IDistributedCacheminnet :

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddDistributedMemoryCache();

builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromSeconds(10);
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseSession();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

Föregående kod anger en kort tidsgräns för att förenkla testningen.

Ordningen på mellanprogram är viktig. Ring UseSession efter UseRouting och före och MapRazorPages .MapDefaultControllerRoute Se Ordning för mellanprogram.

HttpContext.Session är tillgängligt när sessionstillståndet har konfigurerats.

HttpContext.Session kan inte nås innan UseSession har anropats.

Det går inte att skapa en ny session med en ny session cookie när appen har börjat skriva till svarsströmmen. Undantaget registreras i webbserverloggen och visas inte i webbläsaren.

Läsa in sessionstillstånd asynkront

Standardprovidern för sessioner i ASP.NET Core läser in sessionsposter från den underliggande IDistributedCache lagringsplatsen asynkront endast om ISession.LoadAsync metoden uttryckligen anropas före , eller TryGetValue metodernaSetRemove. Om LoadAsync den inte anropas först läses den underliggande sessionsposten in synkront, vilket kan medföra en prestandaförsämring i stor skala.

Om du vill att appar ska framtvinga det här mönstret omsluter DistributedSessionStore du implementeringarna och DistributedSession med versioner som utlöser ett undantag om LoadAsync metoden inte anropas före TryGetValue, Set, eller Remove. Registrera de omslutna versionerna i services containern.

Alternativ för session

Om du vill åsidosätta standardinställningarna för sessionen använder du SessionOptions.

Alternativ Beskrivning
Cookie Bestämmer vilka inställningar som används för att skapa cookie. Name Standardvärdet är SessionDefaults.CookieName (.AspNetCore.Session). Path Standardvärdet är SessionDefaults.CookiePath (/). SameSite Standardvärdet är SameSiteMode.Lax (1). HttpOnly standardvärdet true. IsEssential standardvärdet false.
IdleTimeout Anger IdleTimeout hur länge sessionen kan vara inaktiv innan dess innehåll avbryts. Varje sessionsåtkomst återställer tidsgränsen. Den här inställningen gäller endast för innehållet i sessionen, inte .cookie Standardvärdet är 20 minuter.
IOTimeout Den maximala tid som tillåts för att läsa in en session från butiken eller för att checka in den i butiken igen. Den här inställningen kanske bara gäller för asynkrona åtgärder. Den här tidsgränsen kan inaktiveras med hjälp av InfiniteTimeSpan. Standardvärdet är 1 minut.

Sessionen använder a cookie för att spåra och identifiera begäranden från en enda webbläsare. Som standard heter cookiedetta .AspNetCore.Session och använder sökvägen /. Eftersom standardvärdet cookie inte anger någon domän görs det inte tillgängligt för skriptet på klientsidan på sidan (eftersom HttpOnly standardvärdet är true).

Om du vill åsidosätta cookie standardinställningarna för sessionen använder du SessionOptions:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddDistributedMemoryCache();

builder.Services.AddSession(options =>
{
    options.Cookie.Name = ".AdventureWorks.Session";
    options.IdleTimeout = TimeSpan.FromSeconds(10);
    options.Cookie.IsEssential = true;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseSession();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

Appen använder IdleTimeout egenskapen för att avgöra hur länge en session kan vara inaktiv innan dess innehåll i serverns cache avbryts. Den här egenskapen är oberoende av förfallodatumet cookie . Varje begäran som passerar genom sessionens mellanprogram återställer tidsgränsen.

Sessionstillståndet är inte låsande. Om två begäranden samtidigt försöker ändra innehållet i en session åsidosätter den sista begäran den första. Session genomförs som en sammanhängande session, vilket innebär att allt innehåll lagras tillsammans. När två begäranden försöker ändra olika sessionsvärden kan den sista begäran åsidosätta sessionsändringar som gjorts av den första.

Ange och hämta sessionsvärden

Sessionstillstånd nås från en Razor Pages-klass PageModel eller MVC-klass Controller med HttpContext.Session. Den här egenskapen är en ISession implementering.

Implementeringen ISession innehåller flera tilläggsmetoder för att ange och hämta heltals- och strängvärden. Tilläggsmetoderna finns i Microsoft.AspNetCore.Http namnområdet.

ISession Metoder för tillägg:

I följande exempel hämtas sessionsvärdet för IndexModel.SessionKeyName nyckeln (_Name i exempelappen) på en Razor sidsida:

@page
@using Microsoft.AspNetCore.Http
@model IndexModel

...

Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)

I följande exempel visas hur du anger och hämtar ett heltal och en sträng:

public class IndexModel : PageModel
{
    public const string SessionKeyName = "_Name";
    public const string SessionKeyAge = "_Age";

    private readonly ILogger<IndexModel> _logger;

    public IndexModel(ILogger<IndexModel> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
        {
            HttpContext.Session.SetString(SessionKeyName, "The Doctor");
            HttpContext.Session.SetInt32(SessionKeyAge, 73);
        }
        var name = HttpContext.Session.GetString(SessionKeyName);
        var age = HttpContext.Session.GetInt32(SessionKeyAge).ToString();

        _logger.LogInformation("Session Name: {Name}", name);
        _logger.LogInformation("Session Age: {Age}", age);
    }
}

Följande kod visar sessionsvärdena på en Razor sida:

@page
@model PrivacyModel
@{
    ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<div class="text-center">
<p><b>Name:</b> @HttpContext.Session.GetString("_Name");<b>Age:

</b> @HttpContext.Session.GetInt32("_Age").ToString()</p>
</div>


Alla sessionsdata måste serialiseras för att möjliggöra ett scenario med distribuerad cache, även när du använder den minnesinterna cachen. Serialiserare av strängar och heltal tillhandahålls av tilläggsmetoderna ISessionför . Komplexa typer måste serialiseras av användaren med hjälp av en annan mekanism, till exempel JSON.

Använd följande exempelkod för att serialisera objekt:

public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonSerializer.Serialize(value));
    }

    public static T? Get<T>(this ISession session, string key)
    {
        var value = session.GetString(key);
        return value == null ? default : JsonSerializer.Deserialize<T>(value);
    }
}

I följande exempel visas hur du anger och hämtar ett serialiserbart objekt med SessionExtensions klassen:

using Microsoft.AspNetCore.Mvc.RazorPages;
using Web.Extensions;    // SessionExtensions

namespace SessionSample.Pages
{
    public class Index6Model : PageModel
    {
        const string SessionKeyTime = "_Time";
        public string? SessionInfo_SessionTime { get; private set; }
        private readonly ILogger<Index6Model> _logger;

        public Index6Model(ILogger<Index6Model> logger)
        {
            _logger = logger;
        }

        public void OnGet()
        {
            var currentTime = DateTime.Now;

            // Requires SessionExtensions from sample.
            if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
            {
                HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
            }
            _logger.LogInformation("Current Time: {Time}", currentTime);
            _logger.LogInformation("Session Time: {Time}", 
                           HttpContext.Session.Get<DateTime>(SessionKeyTime));

        }
    }
}

Varning

Att lagra ett live-objekt i sessionen bör användas med försiktighet, eftersom det finns många problem som kan uppstå med serialiserade objekt. Mer information finns i Sessioner ska tillåtas att lagra objekt (dotnet/aspnetcore #18159).

TempData

ASP.NET Core exponerar Razor sidorna TempData eller Controller TempData. Den här egenskapen lagrar data tills de läses i en annan begäran. Metoderna Keep(String) och Peek(string) kan användas för att undersöka data utan borttagning i slutet av begäran. Keep markerar alla objekt i ordlistan för kvarhållning. TempData vara:

  • Användbart för omdirigering när data krävs för mer än en enda begäran.
  • Implementeras av leverantörer med hjälp av TempData antingen cookies eller sessionstillstånd.

TempData-exempel

Tänk på följande sida som skapar en kund:

public class CreateModel : PageModel
{
    private readonly RazorPagesContactsContext _context;

    public CreateModel(RazorPagesContactsContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [TempData]
    public string Message { get; set; }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customer.Add(Customer);
        await _context.SaveChangesAsync();
        Message = $"Customer {Customer.Name} added";

        return RedirectToPage("./IndexPeek");
    }
}

Följande sida visas TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

@{
    if (TempData.Peek("Message") != null)
    {
        <h3>Message: @TempData.Peek("Message")</h3>
    }
}

@*Content removed for brevity.*@

I föregående kod, i slutet av begäran, TempData["Message"] tas inte bort eftersom Peek den används. När du uppdaterar sidan visas innehållet i TempData["Message"].

Följande kod liknar föregående kod, men används Keep för att bevara data i slutet av begäran:

@page
@model IndexModel

<h1>Contacts Keep</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
    TempData.Keep("Message");
}

@*Content removed for brevity.*@

Det går inte att navigera mellan sidorna IndexPeek och TempData["Message"].

Följande kod visas TempData["Message"], men tas bort i slutet av begäran: TempData["Message"]

@page
@model IndexModel

<h1>Index no Keep or Peek</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
}

@*Content removed for brevity.*@

TempData-leverantörer

Den cookie-baserade TempData-leverantören används som standard för att lagra TempData i cookies.

Data cookie krypteras med IDataProtector, kodad med Base64UrlTextEncoderoch sedan segmenterad. Den maximala cookie storleken är mindre än 4096 byte på grund av kryptering och segmentering. Data cookie komprimeras inte eftersom komprimering av krypterade data kan leda till säkerhetsproblem som och CRIMEBREACH attacker. Mer information om den -baserade TempData-providern cookiefinns i CookieTempDataProvider.

Välj en TempData-leverantör

Att välja en TempData-leverantör innebär flera överväganden, till exempel:

  • Använder appen redan sessionstillstånd? I så fall har användning av TempData-providern för sessionstillstånd ingen extra kostnad för appen utöver storleken på data.
  • Använder appen TempData endast sparsamt för relativt små mängder data, upp till 500 byte? I så fall lägger TempData-providern cookie till en liten kostnad för varje begäran som innehåller TempData. Annars kan TempData-providern för sessionstillstånd vara fördelaktigt för att undvika att skicka en stor mängd data i varje begäran tills TempData har förbrukats.
  • Körs appen i en servergrupp på flera servrar? I så fall krävs ingen ytterligare konfiguration för att använda TempData-providern cookie utanför dataskyddet. Mer information finns i ASP.NET Översikt över grundläggande dataskydd och Nyckellagringsproviders.

De flesta webbklienter, till exempel webbläsare, tillämpar gränser för den maximala storleken för varje cookie och det totala antalet cookies. När du använder TempData-providern cookie kontrollerar du att appen inte överskrider dessa gränser. Överväg den totala storleken på data. Ta hänsyn till ökningar i cookie storlek på grund av kryptering och segmentering.

Konfigurera TempData-providern

Den cookie-baserade TempData-providern är aktiverad som standard.

Om du vill aktivera den sessionsbaserade TempData-providern AddSessionStateTempDataProvider använder du tilläggsmetoden. Endast ett anrop till AddSessionStateTempDataProvider krävs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages()
                    .AddSessionStateTempDataProvider();
builder.Services.AddControllersWithViews()
                    .AddSessionStateTempDataProvider();

builder.Services.AddSession();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.UseSession();

app.MapRazorPages();
app.MapDefaultControllerRoute();

app.Run();

Förfrågningssträngar

En begränsad mängd data kan skickas från en begäran till en annan genom att lägga till den i den nya begärans frågesträng. Detta är användbart för att avbilda tillstånd på ett beständigt sätt som gör att länkar med inbäddat tillstånd kan delas via e-post eller sociala nätverk. Eftersom URL-frågesträngar är offentliga ska du aldrig använda frågesträngar för känsliga data.

Förutom oavsiktlig delning kan inkludering av data i frågesträngar exponera appen för CSRF-attacker (Cross-Site Request Forgery). Alla bevarade sessionstillstånd måste skydda mot CSRF-attacker. Mer information finns i Förhindra XSRF-/CSRF-attacker (Cross-Site Request Forgery) i ASP.NET Core.

Dolda fält

Data kan sparas i dolda formulärfält och skickas tillbaka vid nästa begäran. Detta är vanligt i flersidiga formulär. Eftersom klienten potentiellt kan manipulera data måste appen alltid validera om data som lagras i dolda fält.

HttpContext.Items

Samlingen HttpContext.Items används för att lagra data vid bearbetning av en enda begäran. Samlingens innehåll tas bort när en begäran har bearbetats. Samlingen Items används ofta för att tillåta komponenter eller mellanprogram att kommunicera när de fungerar vid olika tidpunkter under en begäran och inte har något direkt sätt att skicka parametrar.

I följande exempel läggs mellanprogram till isVerified i samlingen Items :

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

ILogger logger = app.Logger;

app.Use(async (context, next) =>
{
    // context.Items["isVerified"] is null
    logger.LogInformation($"Before setting: Verified: {context.Items["isVerified"]}");
    context.Items["isVerified"] = true;
    await next.Invoke();
});

app.Use(async (context, next) =>
{
    // context.Items["isVerified"] is true
    logger.LogInformation($"Next: Verified: {context.Items["isVerified"]}");
    await next.Invoke();
});

app.MapGet("/", async context =>
{
    await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
});

app.Run();

För mellanprogram som bara används i en enda app är det osannolikt att användning av en fast string nyckel skulle orsaka en nyckelkollision. Men för att undvika risken för en nyckelkollision helt och hållet kan an object användas som en objektnyckel. Den här metoden är särskilt användbar för mellanprogram som delas mellan appar och har också fördelen att eliminera användningen av nyckelsträngar i koden. I följande exempel visas hur du använder en object nyckel som definierats i en mellanprogramsklass:

public class HttpContextItemsMiddleware
{
    private readonly RequestDelegate _next;
    public static readonly object HttpContextItemsMiddlewareKey = new();

    public HttpContextItemsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        httpContext.Items[HttpContextItemsMiddlewareKey] = "K-9";

        await _next(httpContext);
    }
}

public static class HttpContextItemsMiddlewareExtensions
{
    public static IApplicationBuilder 
        UseHttpContextItemsMiddleware(this IApplicationBuilder app)
    {
        return app.UseMiddleware<HttpContextItemsMiddleware>();
    }
}

Annan kod kan komma åt värdet som lagras med hjälp av nyckeln som exponeras av HttpContext.Items mellanprogramsklassen:

public class Index2Model : PageModel
{
    private readonly ILogger<Index2Model> _logger;

    public Index2Model(ILogger<Index2Model> logger)
    {
        _logger = logger;
    }

    public void OnGet()
    {
        HttpContext.Items
            .TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey,
                out var middlewareSetValue);

        _logger.LogInformation("Middleware value {MV}",
            middlewareSetValue?.ToString() ?? "Middleware value not set!");
    }
}

Cache

Cachelagring är ett effektivt sätt att lagra och hämta data. Appen kan styra livslängden för cachelagrade objekt. För mer information, se Cachning av svar i ASP.NET Core.

Cachelagrade data är inte associerade med en specifik begäran, användare eller session. Cachelagra inte användarspecifika data som kan hämtas av andra användarförfrågningar.

Information om hur du cachelagrar programomfattande data finns i Cachelagra i minnet i ASP.NET Core.

Kontrollerar sessionstillstånd

ISession.IsAvailable är avsett att söka efter tillfälliga fel. Om du anropar IsAvailable innan sessionens mellanprogram körs genereras en InvalidOperationException.

Bibliotek som behöver testa sessionstillgängligheten kan använda HttpContext.Features.Get<ISessionFeature>()?.Session != null.

Vanliga fel

  • "Det går inte att matcha tjänsten för typen 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' vid försök att aktivera 'Microsoft.AspNetCore.Session.DistributedSessionStore'."

    Detta orsakas vanligtvis av att det inte går att konfigurera minst en IDistributedCache implementering. Mer information finns i Distribuerad cachelagring i ASP.NET Core och Cache i minnet i ASP.NET Core.

Om sessionens mellanprogram inte kan bevara en session:

  • Mellanprogrammet loggar undantaget och begäran fortsätter normalt.
  • Detta leder till oförutsägbart beteende.

Sessionens mellanprogram kan misslyckas med att bevara en session om lagringsplatsen inte är tillgänglig. En användare lagrar till exempel en kundvagn i sessionen. Användaren lägger till ett objekt i kundvagnen, men incheckningen misslyckas. Appen känner inte till felet så den rapporterar till användaren att varan har lagts till i kundvagnen, vilket inte är sant.

Den rekommenderade metoden för att söka efter fel är att anropa await feature.Session.CommitAsync när appen är klar med att skriva till sessionen. CommitAsync Genererar ett undantag om lagringsplatsen inte är tillgänglig. Om CommitAsync det misslyckas kan appen bearbeta undantaget. LoadAsync Genereras under samma förhållanden när datalagret inte är tillgängligt.

Ytterligare resurser

Visa eller ladda ned exempelkod (hur du laddar ned)

Host ASP.NET Core i ett webbserverkluster

Av Rick Anderson, Kirk Larkin och Diana LaRose

HTTP är ett tillståndslöst protokoll. Som standard är HTTP-begäranden oberoende meddelanden som inte behåller användarvärden. I den här artikeln beskrivs flera metoder för att bevara användardata mellan begäranden.

Visa eller ladda ned exempelkod (hur du laddar ned)

Statushantering

Tillstånd kan lagras med hjälp av flera metoder. Varje metod beskrivs senare i den här artikeln.

Metod för lagring Mekanism för förvaring
Cookies HTTP-cookies. Kan innehålla data som lagras med hjälp av appkod på serversidan.
Sessions tillstånd HTTP-cookies och appkod på serversidan
Temporär data (TempData) HTTP-cookies eller sessionstillstånd
Frågesträngar HTTP-frågesträngar
Dolda fält Fält för HTTP-formulär
HttpContext.Items (på engelska) Appkod på serversidan
Cache Appkod på serversidan

SignalR/Blazor Server och kontextbaserad tillståndshantering i HTTP

SignalR appar bör inte använda sessionstillstånd och andra tillståndshanteringsmetoder som förlitar sig på en stabil HTTP-kontext för att lagra information. SignalRAppar kan lagra tillstånd per anslutning iContext.Items hubben. Mer information och alternativa tillståndshanteringsmetoder för Blazor Server appar finns i ASP.NET KärntillståndshanteringBlazor.

Webbkakor

Cookies lagrar data över begäranden. Eftersom cookies skickas med varje begäran bör deras storlek hållas till ett minimum. Helst bör endast en identifierare lagras i en cookie med de data som lagras av appen. De flesta webbläsare begränsar cookie storleken till 4096 byte. Endast ett begränsat antal cookies är tillgängliga för varje domän.

Eftersom cookies kan manipuleras måste de valideras av appen. Cookies kan raderas av användare och upphöra att gälla på klienter. Cookies är dock i allmänhet den mest varaktiga formen av datapersistens på klienten.

Cookies används ofta för personalisering, där innehållet anpassas för en känd användare. Användaren identifieras och autentiseras i de flesta fall inte. De cookie kan lagra användarens namn, kontonamn eller unikt användar-ID, till exempel ett GUID. De cookie kan användas för att komma åt användarens personliga inställningar, till exempel deras föredragna bakgrundsfärg på webbplatsen.

Se EU:s allmänna dataskyddsförordning (GDPR) när du utfärdar cookies och hanterar integritetsfrågor. Mer information finns i Stöd för den allmänna dataskyddsförordningen (GDPR) i ASP.NET Core.

Sessions tillstånd

Sessionstillstånd är ett ASP.NET kärnscenario för lagring av användardata medan användaren bläddrar i en webbapp. Sessionstillstånd använder ett arkiv som underhålls av appen för att bevara data mellan begäranden från en klient. Sessionsdata backas upp av ett cacheminne och betraktas som tillfälliga data. Webbplatsen bör fortsätta att fungera utan sessionsdata. Kritiska programdata bör lagras i användardatabasen och cachelagras i sessionen endast som en prestandaoptimering.

Sessionen stöds inte i SignalR appar eftersom en SignalR hubb kan köras oberoende av en HTTP-kontext. Detta kan till exempel inträffa när en lång avsökningsbegäran hålls öppen av en hubb utöver livslängden för begärans HTTP-kontext.

ASP.NET Core upprätthåller sessionstillståndet genom att tillhandahålla en till klienten cookie som innehåller ett sessions-ID. Sessions-ID cookie :

  • Skickas till appen med varje begäran.
  • Används av appen för att hämta sessionsdata.

Sessionstillståndet uppvisar följande beteenden:

  • Sessionen cookie är specifik för webbläsaren. Sessioner delas inte mellan webbläsare.
  • Sessionscookies raderas när webbläsarsessionen avslutas.
  • Om en cookie tas emot för en session som har upphört att gälla skapas en ny session som använder samma session cookie.
  • Tomma sessioner behålls inte. Sessionen måste ha minst ett värde inställt för att bevara sessionen mellan begäranden. När en session inte behålls genereras ett nytt sessions-ID för varje ny begäran.
  • Appen behåller en session under en begränsad tid efter den senaste begäran. Appen ställer antingen in tidsgränsen för sessionen eller använder standardvärdet 20 minuter. Sessionstillstånd är idealiskt för att lagra användardata:
    • Det är specifikt för en viss session.
    • Där data inte kräver permanent lagring mellan sessioner.
  • Sessionsdata tas bort antingen när implementeringen ISession.Clear anropas eller när sessionen upphör att gälla.
  • Det finns ingen standardmekanism för att informera appkoden om att en klientwebbläsare har stängts eller när sessionen cookie tas bort eller har upphört att gälla på klienten.
  • Sessionstillståndscookies markeras inte som nödvändiga som standard. Sessionstillståndet fungerar inte om inte spårning tillåts av webbplatsbesökaren. Mer information finns i Stöd för den allmänna dataskyddsförordningen (GDPR) i ASP.NET Core.

Varning

Lagra inte känsliga data i sessionstillstånd. Användaren kanske inte stänger webbläsaren och rensar sessionen cookie. Vissa webbläsare har giltiga sessionscookies i alla webbläsarfönster. En session kanske inte är begränsad till en enda användare. Nästa användare kan fortsätta att bläddra i appen med samma session cookie.

Providern för minnesintern cache lagrar sessionsdata i minnet på den server där appen finns. I ett servergruppsscenario:

Konfigurera sessionstillstånd

Paketet Microsoft.AspNetCore.Session :

  • Ingår implicit i ramverket.
  • Tillhandahåller mellanprogram för att hantera sessionstillstånd.

Om du vill aktivera mellanprogram för sessionen Startup måste du innehålla:

Följande kod visar hur du ställer in den minnesinterna sessionsprovidern med en standardimplementering i IDistributedCacheminnet :

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDistributedMemoryCache();

        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromSeconds(10);
            options.Cookie.HttpOnly = true;
            options.Cookie.IsEssential = true;
        });

        services.AddControllersWithViews();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.UseSession();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapRazorPages();
        });
    }
}

Föregående kod anger en kort tidsgräns för att förenkla testningen.

Ordningen på mellanprogram är viktig. Ring UseSession efter UseRouting och före UseEndpoints. Se Ordning för mellanprogram.

HttpContext.Session är tillgängligt när sessionstillståndet har konfigurerats.

HttpContext.Session kan inte nås innan UseSession har anropats.

Det går inte att skapa en ny session med en ny session cookie när appen har börjat skriva till svarsströmmen. Undantaget registreras i webbserverloggen och visas inte i webbläsaren.

Läsa in sessionstillstånd asynkront

Standardprovidern för sessioner i ASP.NET Core läser in sessionsposter från den underliggande IDistributedCache lagringsplatsen asynkront endast om ISession.LoadAsync metoden uttryckligen anropas före , eller TryGetValue metodernaSetRemove. Om LoadAsync den inte anropas först läses den underliggande sessionsposten in synkront, vilket kan medföra en prestandaförsämring i stor skala.

Om du vill att appar ska framtvinga det här mönstret omsluter DistributedSessionStore du implementeringarna och DistributedSession med versioner som utlöser ett undantag om LoadAsync metoden inte anropas före TryGetValue, Set, eller Remove. Registrera de omslutna versionerna i services containern.

Alternativ för session

Om du vill åsidosätta standardinställningarna för sessionen använder du SessionOptions.

Alternativ Beskrivning
Cookie Bestämmer vilka inställningar som används för att skapa cookie. Name Standardvärdet är SessionDefaults.CookieName (.AspNetCore.Session). Path Standardvärdet är SessionDefaults.CookiePath (/). SameSite Standardvärdet är SameSiteMode.Lax (1). HttpOnly standardvärdet true. IsEssential standardvärdet false.
IdleTimeout Anger IdleTimeout hur länge sessionen kan vara inaktiv innan dess innehåll avbryts. Varje sessionsåtkomst återställer tidsgränsen. Den här inställningen gäller endast för innehållet i sessionen, inte .cookie Standardvärdet är 20 minuter.
IOTimeout Den maximala tid som tillåts för att läsa in en session från butiken eller för att checka in den i butiken igen. Den här inställningen kanske bara gäller för asynkrona åtgärder. Den här tidsgränsen kan inaktiveras med hjälp av InfiniteTimeSpan. Standardvärdet är 1 minut.

Sessionen använder a cookie för att spåra och identifiera begäranden från en enda webbläsare. Som standard heter cookiedetta .AspNetCore.Session och använder sökvägen /. Eftersom standardvärdet cookie inte anger någon domän görs det inte tillgängligt för skriptet på klientsidan på sidan (eftersom HttpOnly standardvärdet är true).

Om du vill åsidosätta cookie standardinställningarna för sessionen använder du SessionOptions:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDistributedMemoryCache();

    services.AddSession(options =>
    {
        options.Cookie.Name = ".AdventureWorks.Session";
        options.IdleTimeout = TimeSpan.FromSeconds(10);
        options.Cookie.IsEssential = true;
    });

    services.AddControllersWithViews();
    services.AddRazorPages();
}

Appen använder IdleTimeout egenskapen för att avgöra hur länge en session kan vara inaktiv innan dess innehåll i serverns cache avbryts. Den här egenskapen är oberoende av förfallodatumet cookie . Varje begäran som passerar genom sessionens mellanprogram återställer tidsgränsen.

Sessionstillståndet är inte låsande. Om två begäranden samtidigt försöker ändra innehållet i en session åsidosätter den sista begäran den första. Session genomförs som en sammanhängande session, vilket innebär att allt innehåll lagras tillsammans. När två begäranden försöker ändra olika sessionsvärden kan den sista begäran åsidosätta sessionsändringar som gjorts av den första.

Ange och hämta sessionsvärden

Sessionstillstånd nås från en Razor Pages-klass PageModel eller MVC-klass Controller med HttpContext.Session. Den här egenskapen är en ISession implementering.

Implementeringen ISession innehåller flera tilläggsmetoder för att ange och hämta heltals- och strängvärden. Tilläggsmetoderna finns i Microsoft.AspNetCore.Http namnområdet.

ISession Metoder för tillägg:

I följande exempel hämtas sessionsvärdet för IndexModel.SessionKeyName nyckeln (_Name i exempelappen) på en Razor sidsida:

@page
@using Microsoft.AspNetCore.Http
@model IndexModel

...

Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)

I följande exempel visas hur du anger och hämtar ett heltal och en sträng:

public class IndexModel : PageModel
{
    public const string SessionKeyName = "_Name";
    public const string SessionKeyAge = "_Age";
    const string SessionKeyTime = "_Time";

    public string SessionInfo_Name { get; private set; }
    public string SessionInfo_Age { get; private set; }
    public string SessionInfo_CurrentTime { get; private set; }
    public string SessionInfo_SessionTime { get; private set; }
    public string SessionInfo_MiddlewareValue { get; private set; }

    public void OnGet()
    {
        // Requires: using Microsoft.AspNetCore.Http;
        if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
        {
            HttpContext.Session.SetString(SessionKeyName, "The Doctor");
            HttpContext.Session.SetInt32(SessionKeyAge, 773);
        }

        var name = HttpContext.Session.GetString(SessionKeyName);
        var age = HttpContext.Session.GetInt32(SessionKeyAge);

Alla sessionsdata måste serialiseras för att möjliggöra ett scenario med distribuerad cache, även när du använder den minnesinterna cachen. Serialiserare av strängar och heltal tillhandahålls av tilläggsmetoderna ISessionför . Komplexa typer måste serialiseras av användaren med hjälp av en annan mekanism, till exempel JSON.

Använd följande exempelkod för att serialisera objekt:

public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonSerializer.Serialize(value));
    }

    public static T Get<T>(this ISession session, string key)
    {
        var value = session.GetString(key);
        return value == null ? default : JsonSerializer.Deserialize<T>(value);
    }
}

I följande exempel visas hur du anger och hämtar ett serialiserbart objekt med SessionExtensions klassen:

// Requires SessionExtensions from sample download.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
    HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}

TempData

ASP.NET Core exponerar Razor sidorna TempData eller Controller TempData. Den här egenskapen lagrar data tills de läses i en annan begäran. Metoderna Keep(String) och Peek(string) kan användas för att undersöka data utan borttagning i slutet av begäran. Keep markerar alla objekt i ordlistan för kvarhållning. TempData vara:

  • Användbart för omdirigering när data krävs för mer än en enda begäran.
  • Implementeras av leverantörer med hjälp av TempData antingen cookies eller sessionstillstånd.

TempData-exempel

Tänk på följande sida som skapar en kund:

public class CreateModel : PageModel
{
    private readonly RazorPagesContactsContext _context;

    public CreateModel(RazorPagesContactsContext context)
    {
        _context = context;
    }

    public IActionResult OnGet()
    {
        return Page();
    }

    [TempData]
    public string Message { get; set; }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Customer.Add(Customer);
        await _context.SaveChangesAsync();
        Message = $"Customer {Customer.Name} added";

        return RedirectToPage("./IndexPeek");
    }
}

Följande sida visas TempData["Message"]:

@page
@model IndexModel

<h1>Peek Contacts</h1>

@{
    if (TempData.Peek("Message") != null)
    {
        <h3>Message: @TempData.Peek("Message")</h3>
    }
}

@*Content removed for brevity.*@

I föregående kod, i slutet av begäran, TempData["Message"] tas inte bort eftersom Peek den används. När du uppdaterar sidan visas innehållet i TempData["Message"].

Följande kod liknar föregående kod, men används Keep för att bevara data i slutet av begäran:

@page
@model IndexModel

<h1>Contacts Keep</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
    TempData.Keep("Message");
}

@*Content removed for brevity.*@

Det går inte att navigera mellan sidorna IndexPeek och TempData["Message"].

Följande kod visas TempData["Message"], men tas bort i slutet av begäran: TempData["Message"]

@page
@model IndexModel

<h1>Index no Keep or Peek</h1>

@{
    if (TempData["Message"] != null)
    {
        <h3>Message: @TempData["Message"]</h3>
    }
}

@*Content removed for brevity.*@

TempData-leverantörer

Den cookie-baserade TempData-leverantören används som standard för att lagra TempData i cookies.

Data cookie krypteras med IDataProtector, kodad med Base64UrlTextEncoderoch sedan segmenterad. Den maximala cookie storleken är mindre än 4096 byte på grund av kryptering och segmentering. Data cookie komprimeras inte eftersom komprimering av krypterade data kan leda till säkerhetsproblem som och CRIMEBREACH attacker. Mer information om den -baserade TempData-providern cookiefinns i CookieTempDataProvider.

Välj en TempData-leverantör

Att välja en TempData-leverantör innebär flera överväganden, till exempel:

  • Använder appen redan sessionstillstånd? I så fall har användning av TempData-providern för sessionstillstånd ingen extra kostnad för appen utöver storleken på data.
  • Använder appen TempData endast sparsamt för relativt små mängder data, upp till 500 byte? I så fall lägger TempData-providern cookie till en liten kostnad för varje begäran som innehåller TempData. Annars kan TempData-providern för sessionstillstånd vara fördelaktigt för att undvika att skicka en stor mängd data i varje begäran tills TempData har förbrukats.
  • Körs appen i en servergrupp på flera servrar? I så fall krävs ingen ytterligare konfiguration för att använda TempData-providern cookie utanför dataskyddet ( se ASP.NET Översikt över grundläggande dataskydd och Nyckellagringsproviders).

De flesta webbklienter, till exempel webbläsare, tillämpar gränser för den maximala storleken för varje cookie och det totala antalet cookies. När du använder TempData-providern cookie kontrollerar du att appen inte överskrider dessa gränser. Överväg den totala storleken på data. Ta hänsyn till ökningar i cookie storlek på grund av kryptering och segmentering.

Konfigurera TempData-providern

Den cookie-baserade TempData-providern är aktiverad som standard.

Om du vill aktivera den sessionsbaserade TempData-providern AddSessionStateTempDataProvider använder du tilläggsmetoden. Endast ett anrop till AddSessionStateTempDataProvider krävs:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews()
        .AddSessionStateTempDataProvider();
    services.AddRazorPages()
        .AddSessionStateTempDataProvider();

    services.AddSession();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseSession();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
        endpoints.MapRazorPages();
    });
}

Förfrågningssträngar

En begränsad mängd data kan skickas från en begäran till en annan genom att lägga till den i den nya begärans frågesträng. Detta är användbart för att avbilda tillstånd på ett beständigt sätt som gör att länkar med inbäddat tillstånd kan delas via e-post eller sociala nätverk. Eftersom URL-frågesträngar är offentliga ska du aldrig använda frågesträngar för känsliga data.

Förutom oavsiktlig delning kan inkludering av data i frågesträngar exponera appen för CSRF-attacker (Cross-Site Request Forgery). Alla bevarade sessionstillstånd måste skydda mot CSRF-attacker. Mer information finns i Förhindra XSRF-/CSRF-attacker (Cross-Site Request Forgery) i ASP.NET Core.

Dolda fält

Data kan sparas i dolda formulärfält och skickas tillbaka vid nästa begäran. Detta är vanligt i flersidiga formulär. Eftersom klienten potentiellt kan manipulera data måste appen alltid validera om data som lagras i dolda fält.

HttpContext.Items (på engelska)

Samlingen HttpContext.Items används för att lagra data vid bearbetning av en enda begäran. Samlingens innehåll tas bort när en begäran har bearbetats. Samlingen Items används ofta för att tillåta komponenter eller mellanprogram att kommunicera när de fungerar vid olika tidpunkter under en begäran och inte har något direkt sätt att skicka parametrar.

I följande exempel läggs mellanprogram till isVerified i samlingen Items :

public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
    app.UseRouting();

    app.Use(async (context, next) =>
    {
        logger.LogInformation($"Before setting: Verified: {context.Items["isVerified"]}");
        context.Items["isVerified"] = true;
        await next.Invoke();
    });

    app.Use(async (context, next) =>
    {
        logger.LogInformation($"Next: Verified: {context.Items["isVerified"]}");
        await next.Invoke();
    });

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGet("/", async context =>
        {
            await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
        });
    });
}

För mellanprogram som bara används i en enda app är fasta string nycklar acceptabla. Mellanprogram som delas mellan appar bör använda unika objektnycklar för att undvika tangentkollisioner. I följande exempel visas hur du använder en unik objektnyckel som definierats i en mellanprogramsklass:

public class HttpContextItemsMiddleware
{
    private readonly RequestDelegate _next;
    public static readonly object HttpContextItemsMiddlewareKey = new Object();

    public HttpContextItemsMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        httpContext.Items[HttpContextItemsMiddlewareKey] = "K-9";

        await _next(httpContext);
    }
}

public static class HttpContextItemsMiddlewareExtensions
{
    public static IApplicationBuilder 
        UseHttpContextItemsMiddleware(this IApplicationBuilder app)
    {
        return app.UseMiddleware<HttpContextItemsMiddleware>();
    }
}

Annan kod kan komma åt värdet som lagras med hjälp av nyckeln som exponeras av HttpContext.Items mellanprogramsklassen:

HttpContext.Items
    .TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey, 
        out var middlewareSetValue);
SessionInfo_MiddlewareValue = 
    middlewareSetValue?.ToString() ?? "Middleware value not set!";

Den här metoden har också fördelen att eliminera användningen av nyckelsträngar i koden.

Cache

Cachelagring är ett effektivt sätt att lagra och hämta data. Appen kan styra livslängden för cachelagrade objekt. För mer information, se Cachning av svar i ASP.NET Core.

Cachelagrade data är inte associerade med en specifik begäran, användare eller session. Cachelagra inte användarspecifika data som kan hämtas av andra användarförfrågningar.

Information om hur du cachelagrar programomfattande data finns i Cachelagra i minnet i ASP.NET Core.

Vanliga fel

  • "Det går inte att matcha tjänsten för typen 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' vid försök att aktivera 'Microsoft.AspNetCore.Session.DistributedSessionStore'."

    Detta orsakas vanligtvis av att det inte går att konfigurera minst en IDistributedCache implementering. Mer information finns i Distribuerad cachelagring i ASP.NET Core och Cache i minnet i ASP.NET Core.

Om sessionens mellanprogram inte kan bevara en session:

  • Mellanprogrammet loggar undantaget och begäran fortsätter normalt.
  • Detta leder till oförutsägbart beteende.

Sessionens mellanprogram kan misslyckas med att bevara en session om lagringsplatsen inte är tillgänglig. En användare lagrar till exempel en kundvagn i sessionen. Användaren lägger till ett objekt i kundvagnen, men incheckningen misslyckas. Appen känner inte till felet så den rapporterar till användaren att varan har lagts till i kundvagnen, vilket inte är sant.

Den rekommenderade metoden för att söka efter fel är att anropa await feature.Session.CommitAsync när appen är klar med att skriva till sessionen. CommitAsync Genererar ett undantag om lagringsplatsen inte är tillgänglig. Om CommitAsync det misslyckas kan appen bearbeta undantaget. LoadAsync Genereras under samma förhållanden när datalagret inte är tillgängligt.

Ytterligare resurser

Host ASP.NET Core i ett webbserverkluster