Zarządzanie sesjami i stanami na platformie ASP.NET Core
Autor : Rick Anderson, Kirk Larkin i Diana LaRose
HTTP to protokół bezstanowy. Domyślnie żądania HTTP są niezależnymi komunikatami, które nie zachowują wartości użytkowników. W tym artykule opisano kilka metod zachowywania danych użytkownika między żądaniami.
Aby uzyskać Blazor wskazówki dotyczące zarządzania stanem, które dodaje lub zastępuje wskazówki zawarte w tym artykule, zobacz ASP.NET Podstawowe Blazor zarządzanie stanem.
Zarządzanie stanem
Stan można przechowywać przy użyciu kilku metod. Każde podejście zostało opisane w dalszej części tego artykułu.
Podejście do magazynu | Mechanizm magazynowania |
---|---|
Plik cookie | Pliki cookie HTTP. Może zawierać dane przechowywane przy użyciu kodu aplikacji po stronie serwera. |
Stan sesji | Pliki cookie HTTP i kod aplikacji po stronie serwera |
TempData | Pliki cookie lub stan sesji HTTP |
Ciągi zapytań | Ciągi zapytań HTTP |
Ukryte pola | Pola formularza HTTP |
HttpContext.Items | Kod aplikacji po stronie serwera |
Cache | Kod aplikacji po stronie serwera |
SignalR/Blazor Server i zarządzanie stanem opartym na kontekście HTTP
SignalR aplikacje nie powinny używać stanu sesji i innych metod zarządzania stanem, które opierają się na stabilnym kontekście HTTP do przechowywania informacji. SignalR aplikacje mogą przechowywać stan poszczególnych połączeń w Context.Items
centrum. Aby uzyskać więcej informacji i alternatywnych metod zarządzania stanami dla Blazor Server aplikacji, zobacz ASP.NET Zarządzanie stanem podstawowymBlazor.
Pliki cookie
Pliki cookie przechowują dane między żądaniami. Ponieważ pliki cookie są wysyłane z każdym żądaniem, ich rozmiar powinien być minimalny. W idealnym przypadku tylko identyfikator powinien być przechowywany w cookie obiekcie z danymi przechowywanymi przez aplikację. Większość przeglądarek ogranicza cookie rozmiar do 4096 bajtów. Dla każdej domeny jest dostępna tylko ograniczona liczba plików cookie.
Ponieważ pliki cookie podlegają manipulacji, muszą być weryfikowane przez aplikację. Pliki cookie można usuwać przez użytkowników i wygasać na klientach. Pliki cookie są jednak na ogół najbardziej trwałą formą trwałości danych na kliencie.
Pliki cookie są często używane do personalizacji, gdzie zawartość jest dostosowywana dla znanego użytkownika. Użytkownik jest identyfikowany tylko i nie jest uwierzytelniany w większości przypadków. Może cookie przechowywać nazwę użytkownika, nazwę konta lub unikatowy identyfikator użytkownika, taki jak identyfikator GUID. Może cookie służyć do uzyskiwania dostępu do spersonalizowanych ustawień użytkownika, takich jak preferowany kolor tła witryny internetowej.
Zobacz Ogólne przepisy o ochronie danych (RODO) Unii Europejskiej podczas wydawania plików cookie i radzenia sobie z problemamiprivacy. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.
Stan sesji
Stan sesji to ASP.NET Core scenariusz przechowywania danych użytkownika podczas przeglądania aplikacji internetowej przez użytkownika. Stan sesji używa magazynu obsługiwanego przez aplikację do utrwalania danych między żądaniami od klienta. Dane sesji są wspierane przez pamięć podręczną i uważane za dane efemeryczne. Lokacja powinna nadal działać bez danych sesji. Krytyczne dane aplikacji powinny być przechowywane w bazie danych użytkownika i buforowane w sesji tylko jako optymalizacja wydajności.
Sesja nie jest obsługiwana w SignalR aplikacjach, ponieważ SignalR centrum może być wykonywane niezależnie od kontekstu HTTP. Na przykład może to wystąpić, gdy długie żądanie sondowania jest otwarte przez centrum poza okresem istnienia kontekstu HTTP żądania.
ASP.NET Core utrzymuje stan sesji, podając klientowi, cookie który zawiera identyfikator sesji. Identyfikator cookie sesji:
- Jest wysyłany do aplikacji z każdym żądaniem.
- Jest używany przez aplikację do pobierania danych sesji.
Stan sesji wykazuje następujące zachowania:
- Sesja cookie jest specyficzna dla przeglądarki. Sesje nie są udostępniane w przeglądarkach.
- Pliki cookie sesji są usuwane po zakończeniu sesji przeglądarki.
- cookie Jeśli element zostanie odebrany dla wygasłej sesji, zostanie utworzona nowa sesja, która używa tej samej sesji cookie.
- Puste sesje nie są zachowywane. Sesja musi mieć co najmniej jedną wartość ustawioną, aby utrwała sesję między żądaniami. Gdy sesja nie zostanie zachowana, dla każdego nowego żądania zostanie wygenerowany nowy identyfikator sesji.
- Aplikacja zachowuje sesję przez ograniczony czas po ostatnim żądaniu. Aplikacja ustawia limit czasu sesji lub używa wartości domyślnej 20 minut. Stan sesji jest idealny do przechowywania danych użytkownika:
- Jest to specyficzne dla określonej sesji.
- Gdzie dane nie wymagają trwałego przechowywania między sesjami.
- Dane sesji są usuwane, gdy implementacja ISession.Clear jest wywoływana lub gdy sesja wygaśnie.
- Nie ma domyślnego mechanizmu informowania o kodzie aplikacji, że przeglądarka klienta została zamknięta lub gdy sesja cookie zostanie usunięta lub wygasła na kliencie.
- Pliki cookie stanu sesji nie są domyślnie oznaczone jako istotne. Stan sesji nie jest funkcjonalny, chyba że śledzenie jest dozwolone przez odwiedzających witrynę. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.
- Uwaga: funkcja sesji bez plików cookie z platformy ASP.NET Framework nie jest zastępowana, ponieważ jest uważana za niezabezpieczoną i może prowadzić do ataków naprawy sesji.
Ostrzeżenie
Nie przechowuj poufnych danych w stanie sesji. Użytkownik może nie zamknąć przeglądarki i wyczyścić sesję cookie. Niektóre przeglądarki zachowują prawidłowe pliki cookie sesji w oknach przeglądarki. Sesja może nie być ograniczona do jednego użytkownika. Następny użytkownik może nadal przeglądać aplikację przy użyciu tej samej sesji cookie.
Dostawca pamięci podręcznej w pamięci przechowuje dane sesji w pamięci serwera, na którym znajduje się aplikacja. W scenariuszu farmy serwerów:
- Użyj sesji sticky, aby powiązać każdą sesję z określonym wystąpieniem aplikacji na pojedynczym serwerze. usługa aplikacja systemu Azure używa routingu żądań aplikacji (ARR), aby domyślnie wymuszać sesje sticky. Jednak sesje sticky mogą mieć wpływ na skalowalność i komplikować aktualizacje aplikacji internetowej. Lepszym rozwiązaniem jest użycie rozproszonej pamięci podręcznej Redis lub SQL Server, która nie wymaga sesji sticky. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w usłudze ASP.NET Core.
- Sesja cookie jest szyfrowana za pośrednictwem polecenia IDataProtector. Ochrona danych musi być prawidłowo skonfigurowana do odczytywania plików cookie sesji na każdym komputerze. Aby uzyskać więcej informacji, zobacz ASP.NET Core Data Protection Overview and Key Storage providers (Omówienie podstawowej ochrony danych i dostawcy magazynu kluczy).
Konfigurowanie stanu sesji
Oprogramowanie pośredniczące do zarządzania stanem sesji jest zawarte w strukturze. Aby włączyć oprogramowanie pośredniczące sesji, Program.cs
musi zawierać:
- Dowolna pamięć podręczna IDistributedCache . Implementacja
IDistributedCache
jest używana jako magazyn zapasowy dla sesji. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w usłudze ASP.NET Core. - Wywołanie do AddSession
- Wywołanie do UseSession
Poniższy kod pokazuje, jak skonfigurować dostawcę sesji w pamięci z domyślną implementacją w pamięci :IDistributedCache
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();
Powyższy kod ustawia krótki limit czasu, aby uprościć testowanie.
Kolejność oprogramowania pośredniczącego jest ważna. Wywołaj połączenie UseSession
po UseRouting
i przed MapRazorPages
i MapDefaultControllerRoute
. Zobacz Porządkowanie oprogramowania pośredniczącego.
HttpContext.Session jest dostępny po skonfigurowaniu stanu sesji.
HttpContext.Session
Nie można uzyskać dostępu przed UseSession
wywołaniem.
Nie można utworzyć nowej sesji z nową sesją cookie po rozpoczęciu zapisywania aplikacji w strumieniu odpowiedzi. Wyjątek jest rejestrowany w dzienniku serwera internetowego i nie jest wyświetlany w przeglądarce.
Załaduj stan sesji asynchronicznie
Domyślny dostawca sesji w ASP.NET Core ładuje rekordy sesji z bazowego IDistributedCache magazynu zapasowego asynchronicznie tylko wtedy, gdy ISession.LoadAsync metoda jest jawnie wywoływana przed metodami TryGetValue, Setlub Remove . Jeśli LoadAsync
nie jest wywoływany jako pierwszy, podstawowy rekord sesji jest ładowany synchronicznie, co może spowodować karę za wydajność na dużą skalę.
Aby aplikacje wymuszały ten wzorzec, opakuj implementacje i DistributedSession wersjami, które zgłaszają wyjątek, jeśli LoadAsync
metoda nie jest wywoływana przed TryGetValue
Set
, lub Remove
.DistributedSessionStore Zarejestruj opakowane wersje w kontenerze usług.
Opcje sesji
Aby zastąpić wartości domyślne sesji, użyj polecenia SessionOptions.
Opcja | Opis |
---|---|
Cookie | Określa ustawienia używane do utworzenia elementu cookie. Name wartość domyślna to SessionDefaults.CookieName (.AspNetCore.Session ). Path wartość domyślna to SessionDefaults.CookiePath (/ ). SameSite wartość domyślna to SameSiteMode.Lax (1 ). Właściwość HttpOnly domyślnie przyjmuje wartość true . Właściwość IsEssential domyślnie przyjmuje wartość false . |
IdleTimeout | Wskazuje IdleTimeout , jak długo sesja może być bezczynna, zanim jej zawartość zostanie porzucona. Każdy dostęp do sesji resetuje limit czasu. To ustawienie dotyczy tylko zawartości sesji, a nie .cookie Wartość domyślna to 20 minut. |
IOTimeout | Maksymalny czas ładowania sesji ze sklepu lub zatwierdzenia go z powrotem do magazynu. To ustawienie może dotyczyć tylko operacji asynchronicznych. Ten limit czasu można wyłączyć przy użyciu polecenia InfiniteTimeSpan. Wartość domyślna to 1 minuta. |
Sesja używa elementu cookie do śledzenia i identyfikowania żądań z jednej przeglądarki. Domyślnie jest to cookie nazwa .AspNetCore.Session
i używa ścieżki /
. Ponieważ wartość domyślna cookie nie określa domeny, nie jest ona udostępniana skryptowi po stronie klienta na stronie (ponieważ HttpOnly wartość domyślna to true
).
Aby zastąpić cookie wartości domyślne sesji, użyj polecenia 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();
Aplikacja używa IdleTimeout właściwości , aby określić, jak długo sesja może być bezczynna, zanim jej zawartość w pamięci podręcznej serwera zostanie porzucona. Ta właściwość jest niezależna cookie od wygaśnięcia. Każde żądanie przekazywane przez oprogramowanie pośredniczące sesji resetuje limit czasu.
Stan sesji jest nieblokujący. Jeśli dwa żądania jednocześnie próbują zmodyfikować zawartość sesji, ostatnie żądanie zastępuje pierwsze. Session
program jest implementowany jako spójna sesja, co oznacza, że cała zawartość jest przechowywana razem. Gdy dwa żądania próbują zmodyfikować różne wartości sesji, ostatnie żądanie może zastąpić zmiany sesji wprowadzone przez pierwszy.
Ustawianie i pobieranie wartości sesji
Dostęp do stanu sesji jest uzyskiwany z Razor klasy Pages PageModel lub klasy MVC Controller za pomocą polecenia HttpContext.Session. Ta właściwość jest implementacją ISession .
Implementacja ISession
udostępnia kilka metod rozszerzenia do ustawiania i pobierania wartości całkowitych i ciągów. Metody rozszerzenia znajdują się w Microsoft.AspNetCore.Http przestrzeni nazw.
ISession
metody rozszerzeń:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
Poniższy przykład pobiera wartość sesji dla IndexModel.SessionKeyName
klucza (_Name
w przykładowej Razor aplikacji) na stronie Strony:
@page
@using Microsoft.AspNetCore.Http
@model IndexModel
...
Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
W poniższym przykładzie pokazano, jak ustawić i pobrać liczbę całkowitą oraz ciąg:
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);
}
}
Na stronie są wyświetlane następujące znaczniki Razor sesji:
@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>
Wszystkie dane sesji muszą być serializowane, aby umożliwić scenariusz rozproszonej pamięci podręcznej, nawet w przypadku korzystania z pamięci podręcznej. Serializatory ciągów i liczb całkowitych są dostarczane przez metody ISessionrozszerzenia . Typy złożone muszą być serializowane przez użytkownika przy użyciu innego mechanizmu, takiego jak JSON.
Użyj następującego przykładowego kodu, aby serializować obiekty:
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);
}
}
W poniższym przykładzie pokazano, jak ustawić i pobrać obiekt możliwy do serializacji z klasą SessionExtensions
:
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));
}
}
}
Ostrzeżenie
Przechowywanie obiektu na żywo w sesji powinno być używane z ostrożnością, ponieważ istnieje wiele problemów, które mogą wystąpić z serializowanymi obiektami. Aby uzyskać więcej informacji, zobacz Sesje powinny być dozwolone do przechowywania obiektów (dotnet/aspnetcore #18159).
TempData
ASP.NET Core uwidacznia strony TempData lub Controller TempData.Razor Ta właściwość przechowuje dane, dopóki nie zostanie odczytany w innym żądaniu. Metody Keep(String) i Peek(string) mogą służyć do badania danych bez usuwania na końcu żądania. Zachowaj znaczniki wszystkich elementów w słowniku na potrzeby przechowywania. TempData
jest:
- Przydatne w przypadku przekierowania, gdy dane są wymagane dla więcej niż jednego żądania.
- Zaimplementowane przez
TempData
dostawców przy użyciu plików cookie lub stanu sesji.
Przykłady danych tempData
Rozważmy następującą stronę, która tworzy klienta:
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");
}
}
Na poniższej stronie zostanie wyświetlona następująca TempData["Message"]
strona:
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}
@*Content removed for brevity.*@
W poprzednim znaczniku na końcu żądania nie jest usuwana, TempData["Message"]
ponieważ Peek
jest używana. Odświeżenie strony powoduje wyświetlenie zawartości elementu TempData["Message"]
.
Poniższy znacznik jest podobny do poprzedniego kodu, ale używa go Keep
do zachowania danych na końcu żądania:
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}
@*Content removed for brevity.*@
Nawigowanie między stronami IndexPeek i IndexKeep nie spowoduje usunięcia TempData["Message"]
elementu .
Poniższy kod wyświetla TempData["Message"]
ciąg , ale na końcu żądania TempData["Message"]
jest usuwany:
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}
@*Content removed for brevity.*@
Dostawcy tempData
Dostawca cookietempData oparty na protokole TempData jest domyślnie używany do przechowywania danych TempData w plikach cookie.
Dane cookie są szyfrowane przy użyciu metody , zakodowanej za pomocą IDataProtectorBase64UrlTextEncodermetody , a następnie fragmentowane. Maksymalny cookie rozmiar jest mniejszy niż 4096 bajtów ze względu na szyfrowanie i fragmentowanie. Dane cookie nie są kompresowane, ponieważ kompresowanie zaszyfrowanych danych może prowadzić do problemów z zabezpieczeniami, takich jak CRIME ataki i BREACH . Aby uzyskać więcej informacji na temat opartego na cookiedostawcy TempData, zobacz CookieTempDataProvider.
Wybieranie dostawcy tempData
Wybranie dostawcy TempData obejmuje kilka zagadnień, takich jak:
- Czy aplikacja używa już stanu sesji? Jeśli tak, użycie dostawcy TempData stanu sesji nie ma dodatkowych kosztów dla aplikacji poza rozmiarem danych.
- Czy aplikacja używa tempData tylko stosunkowo małych ilości danych, do 500 bajtów? Jeśli tak, cookie dostawca TempData dodaje niewielki koszt do każdego żądania, które niesie ze sobą tempData. Jeśli nie, dostawca TempData stanu sesji może być korzystny, aby uniknąć zaokrąglania dużej ilości danych w każdym żądaniu, dopóki dane tempData nie zostaną zużyte.
- Czy aplikacja działa w farmie serwerów na wielu serwerach? Jeśli tak, nie ma dodatkowej konfiguracji wymaganej cookie do korzystania z dostawcy TempData poza ochroną danych. Aby uzyskać więcej informacji, zobacz ASP.NET Core Data Protection Overview and Key Storage providers (Omówienie podstawowej ochrony danych i dostawcy magazynu kluczy).
Większość klientów internetowych, takich jak przeglądarki internetowe, wymusza limity maksymalnego rozmiaru każdego cookie i łączną liczbę plików cookie. W przypadku korzystania z dostawcy cookie TempData sprawdź, czy aplikacja nie przekroczy tych limitów. Rozważ łączny rozmiar danych. Uwzględnij wzrost rozmiaru cookie ze względu na szyfrowanie i fragmentowanie.
Konfigurowanie dostawcy TempData
Dostawca cookieTempData oparty na protokole TempData jest domyślnie włączony.
Aby włączyć dostawcę TempData opartego na sesji, użyj AddSessionStateTempDataProvider metody rozszerzenia. Wymagane jest tylko jedno wywołanie AddSessionStateTempDataProvider
:
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();
Ciągi zapytań
Ograniczoną ilość danych można przekazać z jednego żądania do innego, dodając je do ciągu zapytania nowego żądania. Jest to przydatne w przypadku przechwytywania stanu w trwały sposób, który umożliwia udostępnianie linków ze stanem osadzonym za pośrednictwem poczty e-mail lub sieci społecznościowych. Ponieważ ciągi zapytania adresu URL są publiczne, nigdy nie używaj ciągów zapytań dla poufnych danych.
Oprócz niezamierzonego udostępniania, w tym danych w ciągach zapytań, można uwidocznić aplikację na ataki między witrynami w celu fałszerowania (CSRF ). Każdy zachowany stan sesji musi chronić przed atakami CSRF. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.
Ukryte pola
Dane można zapisywać w ukrytych polach formularza i publikować je ponownie w następnym żądaniu. Jest to często spotykane w formularzach wielostronicowych. Ponieważ klient może potencjalnie manipulować danymi, aplikacja musi zawsze ponownie modyfikować dane przechowywane w ukrytych polach.
HttpContext.Items
Kolekcja HttpContext.Items służy do przechowywania danych podczas przetwarzania pojedynczego żądania. Zawartość kolekcji jest odrzucana po przetworzeniu żądania. Kolekcja Items
jest często używana do zezwalania składnikom lub oprogramowaniem pośredniczącemu na komunikowanie się, gdy działają w różnych punktach w czasie podczas żądania i nie mają bezpośredniego sposobu przekazywania parametrów.
W poniższym przykładzie oprogramowanie pośredniczące dodaje isVerified
do kolekcji 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();
W przypadku oprogramowania pośredniczącego, które jest używane tylko w jednej aplikacji, jest mało prawdopodobne, że użycie stałego string
klucza spowoduje kolizję klucza. Jednak aby uniknąć możliwości całkowitej kolizji klucza, object
można go użyć jako klucza elementu. Takie podejście jest szczególnie przydatne w przypadku oprogramowania pośredniczącego współużytkowanego między aplikacjami, a także zaletą wyeliminowania używania ciągów kluczy w kodzie. W poniższym przykładzie pokazano, jak używać klucza zdefiniowanego object
w klasie oprogramowania pośredniczącego:
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>();
}
}
Inny kod może uzyskać dostęp do wartości przechowywanej przy HttpContext.Items
użyciu klucza uwidocznionego przez klasę oprogramowania pośredniczącego:
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!");
}
}
Pamięć podręczna
Buforowanie to wydajny sposób przechowywania i pobierania danych. Aplikacja może kontrolować okres istnienia buforowanych elementów. Aby uzyskać więcej informacji, zobacz Buforowanie odpowiedzi w programie ASP.NET Core.
Dane buforowane nie są skojarzone z określonym żądaniem, użytkownikiem ani sesją. Nie buforuj danych specyficznych dla użytkownika, które mogą być pobierane przez inne żądania użytkownika.
Aby buforować dane w całej aplikacji, zobacz Buforowanie w pamięci w ASP.NET Core.
Sprawdzanie stanu sesji
ISession.IsAvailable ma na celu sprawdzenie błędów przejściowych. Wywołanie IsAvailable
przed uruchomieniem oprogramowania pośredniczącego sesji zgłasza błąd InvalidOperationException
.
Biblioteki, które muszą przetestować dostępność sesji, mogą używać polecenia HttpContext.Features.Get<ISessionFeature>()?.Session != null
.
Typowe błędy
"Nie można rozpoznać usługi dla typu "Microsoft.Extensions.Caching.Distributed.IDistributedCache" podczas próby aktywowania elementu "Microsoft.AspNetCore.Session.DistributedSessionStore".
Jest to zwykle spowodowane niepowodzeniem konfigurowania co najmniej jednej
IDistributedCache
implementacji. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w ASP.NET Core i Pamięci podręcznej w pamięci w ASP.NET Core.
Jeśli oprogramowanie pośredniczące sesji nie będzie utrwalać sesji:
- Oprogramowanie pośredniczące rejestruje wyjątek, a żądanie jest kontynuowane normalnie.
- Prowadzi to do nieprzewidywalnego zachowania.
Oprogramowanie pośredniczące sesji może zakończyć się niepowodzeniem, jeśli magazyn kopii zapasowych nie jest dostępny. Na przykład użytkownik przechowuje koszyk w sesji. Użytkownik dodaje element do koszyka, ale zatwierdzenie kończy się niepowodzeniem. Aplikacja nie wie o awarii, dlatego zgłasza użytkownikowi, że element został dodany do koszyka, co nie jest prawdą.
Zalecaną metodą sprawdzania błędów jest wywołanie await feature.Session.CommitAsync
podczas zapisywania aplikacji w sesji. CommitAsync zgłasza wyjątek, jeśli magazyn zapasowy jest niedostępny. W przypadku CommitAsync
niepowodzenia aplikacja może przetworzyć wyjątek. LoadAsync zgłasza te same warunki, gdy magazyn danych jest niedostępny.
Dodatkowe zasoby
Autor : Rick Anderson, Kirk Larkin i Diana LaRose
HTTP to protokół bezstanowy. Domyślnie żądania HTTP są niezależnymi komunikatami, które nie zachowują wartości użytkowników. W tym artykule opisano kilka metod zachowywania danych użytkownika między żądaniami.
Wyświetl lub pobierz przykładowy kod (jak pobrać)
Zarządzanie stanem
Stan można przechowywać przy użyciu kilku metod. Każde podejście zostało opisane w dalszej części tego artykułu.
Podejście do magazynu | Mechanizm magazynowania |
---|---|
Plik cookie | Pliki cookie HTTP. Może zawierać dane przechowywane przy użyciu kodu aplikacji po stronie serwera. |
Stan sesji | Pliki cookie HTTP i kod aplikacji po stronie serwera |
TempData | Pliki cookie lub stan sesji HTTP |
Ciągi zapytań | Ciągi zapytań HTTP |
Ukryte pola | Pola formularza HTTP |
HttpContext.Items | Kod aplikacji po stronie serwera |
Cache | Kod aplikacji po stronie serwera |
SignalR/Blazor Server i zarządzanie stanem opartym na kontekście HTTP
SignalR aplikacje nie powinny używać stanu sesji i innych metod zarządzania stanem, które opierają się na stabilnym kontekście HTTP do przechowywania informacji. SignalR aplikacje mogą przechowywać stan poszczególnych połączeń w Context.Items
centrum. Aby uzyskać więcej informacji i alternatywnych metod zarządzania stanami dla Blazor Server aplikacji, zobacz ASP.NET Zarządzanie stanem podstawowymBlazor.
Pliki cookie
Pliki cookie przechowują dane między żądaniami. Ponieważ pliki cookie są wysyłane z każdym żądaniem, ich rozmiar powinien być minimalny. W idealnym przypadku tylko identyfikator powinien być przechowywany w cookie obiekcie z danymi przechowywanymi przez aplikację. Większość przeglądarek ogranicza cookie rozmiar do 4096 bajtów. Dla każdej domeny jest dostępna tylko ograniczona liczba plików cookie.
Ponieważ pliki cookie podlegają manipulacji, muszą być weryfikowane przez aplikację. Pliki cookie można usuwać przez użytkowników i wygasać na klientach. Pliki cookie są jednak na ogół najbardziej trwałą formą trwałości danych na kliencie.
Pliki cookie są często używane do personalizacji, gdzie zawartość jest dostosowywana dla znanego użytkownika. Użytkownik jest identyfikowany tylko i nie jest uwierzytelniany w większości przypadków. Może cookie przechowywać nazwę użytkownika, nazwę konta lub unikatowy identyfikator użytkownika, taki jak identyfikator GUID. Może cookie służyć do uzyskiwania dostępu do spersonalizowanych ustawień użytkownika, takich jak preferowany kolor tła witryny internetowej.
Zobacz Ogólne przepisy o ochronie danych (RODO) Unii Europejskiej podczas wydawania plików cookie i radzenia sobie z problemamiprivacy. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.
Stan sesji
Stan sesji to ASP.NET Core scenariusz przechowywania danych użytkownika podczas przeglądania aplikacji internetowej przez użytkownika. Stan sesji używa magazynu obsługiwanego przez aplikację do utrwalania danych między żądaniami od klienta. Dane sesji są wspierane przez pamięć podręczną i uważane za dane efemeryczne. Lokacja powinna nadal działać bez danych sesji. Krytyczne dane aplikacji powinny być przechowywane w bazie danych użytkownika i buforowane w sesji tylko jako optymalizacja wydajności.
Sesja nie jest obsługiwana w SignalR aplikacjach, ponieważ SignalR centrum może być wykonywane niezależnie od kontekstu HTTP. Na przykład może to wystąpić, gdy długie żądanie sondowania jest otwarte przez centrum poza okresem istnienia kontekstu HTTP żądania.
ASP.NET Core utrzymuje stan sesji, podając klientowi, cookie który zawiera identyfikator sesji. Identyfikator cookie sesji:
- Jest wysyłany do aplikacji z każdym żądaniem.
- Jest używany przez aplikację do pobierania danych sesji.
Stan sesji wykazuje następujące zachowania:
- Sesja cookie jest specyficzna dla przeglądarki. Sesje nie są udostępniane w przeglądarkach.
- Pliki cookie sesji są usuwane po zakończeniu sesji przeglądarki.
- cookie Jeśli element zostanie odebrany dla wygasłej sesji, zostanie utworzona nowa sesja, która używa tej samej sesji cookie.
- Puste sesje nie są zachowywane. Sesja musi mieć co najmniej jedną wartość ustawioną, aby utrwała sesję między żądaniami. Gdy sesja nie zostanie zachowana, dla każdego nowego żądania zostanie wygenerowany nowy identyfikator sesji.
- Aplikacja zachowuje sesję przez ograniczony czas po ostatnim żądaniu. Aplikacja ustawia limit czasu sesji lub używa wartości domyślnej 20 minut. Stan sesji jest idealny do przechowywania danych użytkownika:
- Jest to specyficzne dla określonej sesji.
- Gdzie dane nie wymagają trwałego przechowywania między sesjami.
- Dane sesji są usuwane, gdy implementacja ISession.Clear jest wywoływana lub gdy sesja wygaśnie.
- Nie ma domyślnego mechanizmu informowania o kodzie aplikacji, że przeglądarka klienta została zamknięta lub gdy sesja cookie zostanie usunięta lub wygasła na kliencie.
- Pliki cookie stanu sesji nie są domyślnie oznaczone jako istotne. Stan sesji nie jest funkcjonalny, chyba że śledzenie jest dozwolone przez odwiedzających witrynę. Aby uzyskać więcej informacji, zobacz Ogólne wsparcie dotyczące rozporządzenia o ochronie danych (RODO) w ASP.NET Core.
Ostrzeżenie
Nie przechowuj poufnych danych w stanie sesji. Użytkownik może nie zamknąć przeglądarki i wyczyścić sesję cookie. Niektóre przeglądarki zachowują prawidłowe pliki cookie sesji w oknach przeglądarki. Sesja może nie być ograniczona do jednego użytkownika. Następny użytkownik może nadal przeglądać aplikację przy użyciu tej samej sesji cookie.
Dostawca pamięci podręcznej w pamięci przechowuje dane sesji w pamięci serwera, na którym znajduje się aplikacja. W scenariuszu farmy serwerów:
- Użyj sesji sticky, aby powiązać każdą sesję z określonym wystąpieniem aplikacji na pojedynczym serwerze. usługa aplikacja systemu Azure używa routingu żądań aplikacji (ARR), aby domyślnie wymuszać sesje sticky. Jednak sesje sticky mogą mieć wpływ na skalowalność i komplikować aktualizacje aplikacji internetowej. Lepszym rozwiązaniem jest użycie rozproszonej pamięci podręcznej Redis lub SQL Server, która nie wymaga sesji sticky. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w usłudze ASP.NET Core.
- Sesja cookie jest szyfrowana za pośrednictwem polecenia IDataProtector. Ochrona danych musi być prawidłowo skonfigurowana do odczytywania plików cookie sesji na każdym komputerze. Aby uzyskać więcej informacji, zobacz ASP.NET Core Data Protection Overview and Key Storage providers (Omówienie podstawowej ochrony danych i dostawcy magazynu kluczy).
Konfigurowanie stanu sesji
Pakiet Microsoft.AspNetCore.Session:
- Jest dołączana niejawnie przez platformę.
- Udostępnia oprogramowanie pośredniczące do zarządzania stanem sesji.
Aby włączyć oprogramowanie pośredniczące sesji, Startup
musi zawierać:
- Dowolna pamięć podręczna IDistributedCache . Implementacja
IDistributedCache
jest używana jako magazyn zapasowy dla sesji. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w usłudze ASP.NET Core. - Wywołanie metody AddSession w pliku
ConfigureServices
. - Wywołanie metody UseSession w pliku
Configure
.
Poniższy kod pokazuje, jak skonfigurować dostawcę sesji w pamięci z domyślną implementacją w pamięci :IDistributedCache
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();
});
}
}
Powyższy kod ustawia krótki limit czasu, aby uprościć testowanie.
Kolejność oprogramowania pośredniczącego jest ważna. Wywołaj połączenie UseSession
po UseRouting
i przed UseEndpoints
. Zobacz Porządkowanie oprogramowania pośredniczącego.
HttpContext.Session jest dostępny po skonfigurowaniu stanu sesji.
HttpContext.Session
Nie można uzyskać dostępu przed UseSession
wywołaniem.
Nie można utworzyć nowej sesji z nową sesją cookie po rozpoczęciu zapisywania aplikacji w strumieniu odpowiedzi. Wyjątek jest rejestrowany w dzienniku serwera internetowego i nie jest wyświetlany w przeglądarce.
Załaduj stan sesji asynchronicznie
Domyślny dostawca sesji w ASP.NET Core ładuje rekordy sesji z bazowego IDistributedCache magazynu zapasowego asynchronicznie tylko wtedy, gdy ISession.LoadAsync metoda jest jawnie wywoływana przed metodami TryGetValue, Setlub Remove . Jeśli LoadAsync
nie jest wywoływany jako pierwszy, podstawowy rekord sesji jest ładowany synchronicznie, co może spowodować karę za wydajność na dużą skalę.
Aby aplikacje wymuszały ten wzorzec, opakuj implementacje i DistributedSession wersjami, które zgłaszają wyjątek, jeśli LoadAsync
metoda nie jest wywoływana przed TryGetValue
Set
, lub Remove
.DistributedSessionStore Zarejestruj opakowane wersje w kontenerze usług.
Opcje sesji
Aby zastąpić wartości domyślne sesji, użyj polecenia SessionOptions.
Opcja | Opis |
---|---|
Cookie | Określa ustawienia używane do utworzenia elementu cookie. Name wartość domyślna to SessionDefaults.CookieName (.AspNetCore.Session ). Path wartość domyślna to SessionDefaults.CookiePath (/ ). SameSite wartość domyślna to SameSiteMode.Lax (1 ). Właściwość HttpOnly domyślnie przyjmuje wartość true . Właściwość IsEssential domyślnie przyjmuje wartość false . |
IdleTimeout | Wskazuje IdleTimeout , jak długo sesja może być bezczynna, zanim jej zawartość zostanie porzucona. Każdy dostęp do sesji resetuje limit czasu. To ustawienie dotyczy tylko zawartości sesji, a nie .cookie Wartość domyślna to 20 minut. |
IOTimeout | Maksymalny czas ładowania sesji ze sklepu lub zatwierdzenia go z powrotem do magazynu. To ustawienie może dotyczyć tylko operacji asynchronicznych. Ten limit czasu można wyłączyć przy użyciu polecenia InfiniteTimeSpan. Wartość domyślna to 1 minuta. |
Sesja używa elementu cookie do śledzenia i identyfikowania żądań z jednej przeglądarki. Domyślnie jest to cookie nazwa .AspNetCore.Session
i używa ścieżki /
. Ponieważ wartość domyślna cookie nie określa domeny, nie jest ona udostępniana skryptowi po stronie klienta na stronie (ponieważ HttpOnly wartość domyślna to true
).
Aby zastąpić cookie wartości domyślne sesji, użyj polecenia 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();
}
Aplikacja używa IdleTimeout właściwości , aby określić, jak długo sesja może być bezczynna, zanim jej zawartość w pamięci podręcznej serwera zostanie porzucona. Ta właściwość jest niezależna cookie od wygaśnięcia. Każde żądanie przekazywane przez oprogramowanie pośredniczące sesji resetuje limit czasu.
Stan sesji jest nieblokujący. Jeśli dwa żądania jednocześnie próbują zmodyfikować zawartość sesji, ostatnie żądanie zastępuje pierwsze. Session
program jest implementowany jako spójna sesja, co oznacza, że cała zawartość jest przechowywana razem. Gdy dwa żądania próbują zmodyfikować różne wartości sesji, ostatnie żądanie może zastąpić zmiany sesji wprowadzone przez pierwszy.
Ustawianie i pobieranie wartości sesji
Dostęp do stanu sesji jest uzyskiwany z Razor klasy Pages PageModel lub klasy MVC Controller za pomocą polecenia HttpContext.Session. Ta właściwość jest implementacją ISession .
Implementacja ISession
udostępnia kilka metod rozszerzenia do ustawiania i pobierania wartości całkowitych i ciągów. Metody rozszerzenia znajdują się w Microsoft.AspNetCore.Http przestrzeni nazw.
ISession
metody rozszerzeń:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
Poniższy przykład pobiera wartość sesji dla IndexModel.SessionKeyName
klucza (_Name
w przykładowej Razor aplikacji) na stronie Strony:
@page
@using Microsoft.AspNetCore.Http
@model IndexModel
...
Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
W poniższym przykładzie pokazano, jak ustawić i pobrać liczbę całkowitą oraz ciąg:
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);
Wszystkie dane sesji muszą być serializowane, aby umożliwić scenariusz rozproszonej pamięci podręcznej, nawet w przypadku korzystania z pamięci podręcznej. Serializatory ciągów i liczb całkowitych są dostarczane przez metody ISessionrozszerzenia . Typy złożone muszą być serializowane przez użytkownika przy użyciu innego mechanizmu, takiego jak JSON.
Użyj następującego przykładowego kodu, aby serializować obiekty:
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);
}
}
W poniższym przykładzie pokazano, jak ustawić i pobrać obiekt możliwy do serializacji z klasą SessionExtensions
:
// Requires SessionExtensions from sample download.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}
TempData
ASP.NET Core uwidacznia strony TempData lub Controller TempData.Razor Ta właściwość przechowuje dane, dopóki nie zostanie odczytany w innym żądaniu. Metody Keep(String) i Peek(string) mogą służyć do badania danych bez usuwania na końcu żądania. Zachowaj znaczniki wszystkich elementów w słowniku na potrzeby przechowywania. TempData
jest:
- Przydatne w przypadku przekierowania, gdy dane są wymagane dla więcej niż jednego żądania.
- Zaimplementowane przez
TempData
dostawców przy użyciu plików cookie lub stanu sesji.
Przykłady danych tempData
Rozważmy następującą stronę, która tworzy klienta:
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");
}
}
Na poniższej stronie zostanie wyświetlona następująca TempData["Message"]
strona:
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}
@*Content removed for brevity.*@
W poprzednim znaczniku na końcu żądania nie jest usuwana, TempData["Message"]
ponieważ Peek
jest używana. Odświeżenie strony powoduje wyświetlenie zawartości elementu TempData["Message"]
.
Poniższy znacznik jest podobny do poprzedniego kodu, ale używa go Keep
do zachowania danych na końcu żądania:
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}
@*Content removed for brevity.*@
Nawigowanie między stronami IndexPeek i IndexKeep nie spowoduje usunięcia TempData["Message"]
elementu .
Poniższy kod wyświetla TempData["Message"]
ciąg , ale na końcu żądania TempData["Message"]
jest usuwany:
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}
@*Content removed for brevity.*@
Dostawcy tempData
Dostawca cookietempData oparty na protokole TempData jest domyślnie używany do przechowywania danych TempData w plikach cookie.
Dane cookie są szyfrowane przy użyciu metody , zakodowanej za pomocą IDataProtectorBase64UrlTextEncodermetody , a następnie fragmentowane. Maksymalny cookie rozmiar jest mniejszy niż 4096 bajtów ze względu na szyfrowanie i fragmentowanie. Dane cookie nie są kompresowane, ponieważ kompresowanie zaszyfrowanych danych może prowadzić do problemów z zabezpieczeniami, takich jak CRIME ataki i BREACH . Aby uzyskać więcej informacji na temat opartego na cookiedostawcy TempData, zobacz CookieTempDataProvider.
Wybieranie dostawcy tempData
Wybranie dostawcy TempData obejmuje kilka zagadnień, takich jak:
- Czy aplikacja używa już stanu sesji? Jeśli tak, użycie dostawcy TempData stanu sesji nie ma dodatkowych kosztów dla aplikacji poza rozmiarem danych.
- Czy aplikacja używa tempData tylko stosunkowo małych ilości danych, do 500 bajtów? Jeśli tak, cookie dostawca TempData dodaje niewielki koszt do każdego żądania, które niesie ze sobą tempData. Jeśli nie, dostawca TempData stanu sesji może być korzystny, aby uniknąć zaokrąglania dużej ilości danych w każdym żądaniu, dopóki dane tempData nie zostaną zużyte.
- Czy aplikacja działa w farmie serwerów na wielu serwerach? Jeśli tak, nie ma dodatkowej konfiguracji wymaganej do korzystania z cookie dostawcy TempData poza ochroną danych (zobacz ASP.NET Core Data Protection Overview and Key Storage providers (Omówienie podstawowego ochrony danych i dostawców magazynu kluczy).
Większość klientów internetowych, takich jak przeglądarki internetowe, wymusza limity maksymalnego rozmiaru każdego cookie i łączną liczbę plików cookie. W przypadku korzystania z dostawcy cookie TempData sprawdź, czy aplikacja nie przekroczy tych limitów. Rozważ łączny rozmiar danych. Uwzględnij wzrost rozmiaru cookie ze względu na szyfrowanie i fragmentowanie.
Konfigurowanie dostawcy TempData
Dostawca cookieTempData oparty na protokole TempData jest domyślnie włączony.
Aby włączyć dostawcę TempData opartego na sesji, użyj AddSessionStateTempDataProvider metody rozszerzenia. Wymagane jest tylko jedno wywołanie AddSessionStateTempDataProvider
:
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();
});
}
Ciągi zapytań
Ograniczoną ilość danych można przekazać z jednego żądania do innego, dodając je do ciągu zapytania nowego żądania. Jest to przydatne w przypadku przechwytywania stanu w trwały sposób, który umożliwia udostępnianie linków ze stanem osadzonym za pośrednictwem poczty e-mail lub sieci społecznościowych. Ponieważ ciągi zapytania adresu URL są publiczne, nigdy nie używaj ciągów zapytań dla poufnych danych.
Oprócz niezamierzonego udostępniania, w tym danych w ciągach zapytań, można uwidocznić aplikację na ataki między witrynami w celu fałszerowania (CSRF ). Każdy zachowany stan sesji musi chronić przed atakami CSRF. Aby uzyskać więcej informacji, zobacz Zapobieganie atakom z fałszowaniem żądań międzywitrynowych (XSRF/CSRF) na platformie ASP.NET Core.
Ukryte pola
Dane można zapisywać w ukrytych polach formularza i publikować je ponownie w następnym żądaniu. Jest to często spotykane w formularzach wielostronicowych. Ponieważ klient może potencjalnie manipulować danymi, aplikacja musi zawsze ponownie modyfikować dane przechowywane w ukrytych polach.
HttpContext.Items
Kolekcja HttpContext.Items służy do przechowywania danych podczas przetwarzania pojedynczego żądania. Zawartość kolekcji jest odrzucana po przetworzeniu żądania. Kolekcja Items
jest często używana do zezwalania składnikom lub oprogramowaniem pośredniczącemu na komunikowanie się, gdy działają w różnych punktach w czasie podczas żądania i nie mają bezpośredniego sposobu przekazywania parametrów.
W poniższym przykładzie oprogramowanie pośredniczące dodaje isVerified
do kolekcji 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"]}");
});
});
}
W przypadku oprogramowania pośredniczącego, które jest używane tylko w jednej aplikacji, stałe string
klucze są akceptowalne. Oprogramowanie pośredniczące współużytkowane między aplikacjami powinno używać unikatowych kluczy obiektów, aby uniknąć kolizji kluczy. W poniższym przykładzie pokazano, jak używać unikatowego klucza obiektu zdefiniowanego w klasie oprogramowania pośredniczącego:
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>();
}
}
Inny kod może uzyskać dostęp do wartości przechowywanej przy HttpContext.Items
użyciu klucza uwidocznionego przez klasę oprogramowania pośredniczącego:
HttpContext.Items
.TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey,
out var middlewareSetValue);
SessionInfo_MiddlewareValue =
middlewareSetValue?.ToString() ?? "Middleware value not set!";
Takie podejście ma również zaletę wyeliminowania używania ciągów kluczy w kodzie.
Pamięć podręczna
Buforowanie to wydajny sposób przechowywania i pobierania danych. Aplikacja może kontrolować okres istnienia buforowanych elementów. Aby uzyskać więcej informacji, zobacz Buforowanie odpowiedzi w programie ASP.NET Core.
Dane buforowane nie są skojarzone z określonym żądaniem, użytkownikiem ani sesją. Nie buforuj danych specyficznych dla użytkownika, które mogą być pobierane przez inne żądania użytkownika.
Aby buforować dane w całej aplikacji, zobacz Buforowanie w pamięci w ASP.NET Core.
Typowe błędy
"Nie można rozpoznać usługi dla typu "Microsoft.Extensions.Caching.Distributed.IDistributedCache" podczas próby aktywowania elementu "Microsoft.AspNetCore.Session.DistributedSessionStore".
Jest to zwykle spowodowane niepowodzeniem konfigurowania co najmniej jednej
IDistributedCache
implementacji. Aby uzyskać więcej informacji, zobacz Buforowanie rozproszone w ASP.NET Core i Pamięci podręcznej w pamięci w ASP.NET Core.
Jeśli oprogramowanie pośredniczące sesji nie będzie utrwalać sesji:
- Oprogramowanie pośredniczące rejestruje wyjątek, a żądanie jest kontynuowane normalnie.
- Prowadzi to do nieprzewidywalnego zachowania.
Oprogramowanie pośredniczące sesji może zakończyć się niepowodzeniem, jeśli magazyn kopii zapasowych nie jest dostępny. Na przykład użytkownik przechowuje koszyk w sesji. Użytkownik dodaje element do koszyka, ale zatwierdzenie kończy się niepowodzeniem. Aplikacja nie wie o awarii, dlatego zgłasza użytkownikowi, że element został dodany do koszyka, co nie jest prawdą.
Zalecaną metodą sprawdzania błędów jest wywołanie await feature.Session.CommitAsync
podczas zapisywania aplikacji w sesji. CommitAsync zgłasza wyjątek, jeśli magazyn zapasowy jest niedostępny. W przypadku CommitAsync
niepowodzenia aplikacja może przetworzyć wyjątek. LoadAsync zgłasza te same warunki, gdy magazyn danych jest niedostępny.