Збільшення часу очікування сеансу для програм ASP.NET Core, запущених у службах IIS
Середовище перевірки роботи
- Візуальна студія
-
- Visual Studio 2022
- ASP.NET Ядро
-
- 8 (Бритвені сторінки, MVC)
- Windows Server
-
- 2022
- ІВС
-
- 10.0
Робоче середовище
Ми тестували його не у всіх випадках, але загалом він має працювати за таких умов:
- Візуальна студія
-
- Все, що може розвинути проєкт ASP.NET Core
- ASP.NET Ядро
-
- Будь-яка версія (MVC, Razor Pages, API)
- Windows Server
-
- Windows Server 2008 або пізнішої версії
- ІВС
-
- 7.0 або новішої версії
Спочатку
Додатки, написані за допомогою ASP.NET Core, працюють на простому сервері під назвою «Kestrel», а тривалість, така як тайм-аут сесії, в основному встановлюється програмно.
Однак, якщо ви розміщуєтеся в службах IIS і запускаєте програму ASP.NET Core, ви можете замінити значення, встановлене в IIS, замість програмних параметрів.
Час очікування сеансу через час простою, описаний у цей час, залежить від параметрів IIS, тому давайте встановимо цей. Однак зауважте, що на час сеансу впливає кілька налаштувань, тому, залежно від налаштувань, час очікування сеансу може не бути продовжено лише за допомогою цього параметра.
Це пояснюється тестовою програмою, але якщо вам потрібен швидкий спосіб її налаштування, будь ласка, перейдіть до кінця цієї сторінки.
Програми входу в систему
Цього разу ми перевіримо операцію в сеансі входу. Ви можете використовувати інші сеанси, але пам'ятайте про тайм-аут за замовчуванням для сеансів.
Для програм входу в систему майже завжди використовуються наступні поради, тому, будь ласка, зверніться до наведених нижче для отримання детальної інформації. Неважливо, чи це Razor Pages, чи MVC.
Ця порада стосується лише тайм-аутів сесій, тому я не буду вдаватися в решту коду. Змініть простір імен відповідно до вашого проекту.
Код проекту Razor Pages
Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
// コンテナにサービスを追加します。
builder.Services.AddRazorPages();
// ※ここから追加
// Cookie による認証スキームを追加する
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
builder.Services.AddAuthorization(options =>
{
// AllowAnonymous 属性が指定されていないすべての画面、アクションなどに対してユーザー認証が必要となる
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
// ※ここまで追加
var app = builder.Build();
// HTTP リクエスト パイプラインを構成します。
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// デフォルトの HSTS 値は 30 日です。 運用シナリオではこれを変更することもできます。https://aka.ms/aspnetcore-hsts を参照してください。
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); // [追加] 認証
app.UseAuthorization(); // 認可
app.MapRazorPages();
app.Run();
Сторінки/Обліковий запис/Login.cshtml.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
namespace SessionTimeoutExtensionIisRazorPages.Pages
{
[AllowAnonymous]
public class LoginModel : PageModel
{
<summary>ユーザー名。</summary>
[BindProperty]
[Required]
[DisplayName("ユーザー名")]
public string UserName { get; set; } = "";
<summary>パスワード。</summary>
[BindProperty]
[Required]
[DataType(DataType.Password)]
[DisplayName("パスワード")]
public string Password { get; set; } = "";
<summary>仮のユーザーデータベースとする。</summary>
private Dictionary<string, string> UserAccounts { get; set; } = new Dictionary<string, string>
{
{ "user1", "password1" },
{ "user2", "password2" },
};
<summary>ログイン処理。</summary>
public async Task<ActionResult> OnPost()
{
// 入力内容にエラーがある場合は処理を中断してエラー表示
if (ModelState.IsValid == false) return Page();
// ユーザーの存在チェックとパスワードチェック (仮実装)
// 本 Tips は Cookie 認証ができるかどうかの確認であるため入力内容やパスワードの厳密なチェックは行っていません
if (UserAccounts.TryGetValue(UserName, out string? getPass) == false || Password != getPass)
{
ModelState.AddModelError("", "ユーザー名またはパスワードが一致しません。");
return Page();
}
// サインインに必要なプリンシパルを作る
var claims = new[] { new Claim(ClaimTypes.Name, UserName) };
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
// 認証クッキーをレスポンスに追加
await HttpContext.SignInAsync(principal);
// ログインが必要な画面にリダイレクトします
return RedirectToPage("/Index");
}
<summary>ログアウト処理。</summary>
public async Task OnGetLogout()
{
// 認証クッキーをレスポンスから削除
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
Сторінки/Обліковий запис/Login.cshtml
@page
@model LoginModel
@{}
<form asp-action="Login">
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<label asp-for="UserName" class="form-label"></label>
<input asp-for="UserName" class="form-control" value="user1" />
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<label asp-for="Password" class="form-label"></label>
<input asp-for="Password" class="form-control" value="password1" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<button type="submit" class="btn btn-primary">ログイン</button>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<a asp-page="/Index">認証が必要な画面へ直接リンク</a>
</div>
</div>
</form>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
Pages/Index.cshtml
@page
@model IndexModel
@{
ViewData["Title"] = "Home page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<p>アクセス日時:@DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")</p>
Pages/Shared/_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - SessionTimeoutExtensionIisRazorPages</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/SessionTimeoutExtensionIisRazorPages.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-page="/Index">SessionTimeoutExtensionIisRazorPages</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
</li>
@* ここから追加 *@
<li class="nav-item">
<a class="nav-link text-dark" asp-page="/Account/Login" asp-page-handler="Logout">ログアウト</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-page="/Account/Login">ログアウトせずログインへ</a>
</li>
@* ここまで追加 *@
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2024 - SessionTimeoutExtensionIisRazorPages - <a asp-area="" asp-page="/Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Код проекту MVC
Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
// コンテナにサービスを追加します。
builder.Services.AddControllersWithViews();
// ※ここから追加
// Cookie による認証スキームを追加する
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
builder.Services.AddAuthorization(options =>
{
// AllowAnonymous 属性が指定されていないすべての画面、アクションなどに対してユーザー認証が必要となる
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
// ※ここまで追加
var app = builder.Build();
// HTTP リクエスト パイプラインを構成します。
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// デフォルトの HSTS 値は 30 日です。 運用シナリオではこれを変更することもできます。https://aka.ms/aspnetcore-hsts を参照してください。
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication(); // [追加] 認証
app.UseAuthorization(); // 認可
app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Моделі/LoginModel.cs
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace SessionTimeoutExtensionIisMvc.Models
{
public class LoginModel
{
<summary>ユーザー名。</summary>
[Required]
[DisplayName("ユーザー名")]
public string UserName { get; set; } = "";
<summary>パスワード。</summary>
[Required]
[DataType(DataType.Password)]
[DisplayName("パスワード")]
public string Password { get; set; } = "";
}
}
Контролери/AccountController.cs
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SessionTimeoutExtensionIisMvc.Models;
using System.Security.Claims;
namespace SessionTimeoutExtensionIisMvc.Controllers
{
<remarks>
<see cref="AllowAnonymous"/> 属性は Cookie 認証していなくてもアクセスできる Action (Controller) であることを示す。
</remarks>
[AllowAnonymous]
public class AccountController : Controller
{
<summary>仮のユーザーデータベースとする。</summary>
private Dictionary<string, string> UserAccounts { get; set; } = new Dictionary<string, string>
{
{ "user1", "password1" },
{ "user2", "password2" },
};
<summary>ログイン画面を表示します。</summary>
public IActionResult Login() => View();
<summary>ログイン処理を実行します。</summary>
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
// 入力内容にエラーがある場合は処理を中断してエラー表示
if (ModelState.IsValid == false) return View(model);
// ユーザーの存在チェックとパスワードチェック (仮実装)
// 本 Tips は Cookie 認証ができるかどうかの確認であるため入力内容やパスワードの厳密なチェックは行っていません
if (UserAccounts.TryGetValue(model.UserName, out string? getPass) == false || model.Password != getPass)
{
ModelState.AddModelError("", "ユーザー名またはパスワードが一致しません。");
return View(model);
}
// サインインに必要なプリンシパルを作る
var claims = new[] { new Claim(ClaimTypes.Name, model.UserName) };
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
// 認証クッキーをレスポンスに追加
await HttpContext.SignInAsync(principal);
// ログインが必要な画面にリダイレクトします
return RedirectToAction(nameof(HomeController.Index), "Home");
}
<summary>ログアウト処理を実行します。</summary>
public async Task<IActionResult> Logout()
{
// 認証クッキーをレスポンスから削除
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// ログイン画面にリダイレクト
return RedirectToAction(nameof(Login));
}
}
}
Перегляди/Обліковий запис/Login.cshtml
@model LoginModel
@{}
<form asp-action="Login">
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<label asp-for="UserName" class="form-label"></label>
<input asp-for="UserName" class="form-control" value="user1" />
<span asp-validation-for="UserName" class="text-danger"></span>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<label asp-for="Password" class="form-label"></label>
<input asp-for="Password" class="form-control" value="password1" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<button type="submit" class="btn btn-primary">ログイン</button>
</div>
</div>
<div class="row m-1 g-3">
<div class="col-sm-6 offset-sm-3">
<a asp-controller="Home" asp-action="Index">認証が必要な画面へ直接リンク</a>
</div>
</div>
</form>
@section Scripts {
@{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}
Перегляди/Головна/Індекс.cshtml
@{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<p>アクセス日時:@DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")</p>
Перегляди/Спільний/_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - SessionTimeoutExtensionIisMvc</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/SessionTimeoutExtensionIisMvc.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">SessionTimeoutExtensionIisMvc</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
@* ここから追加 *@
<li class="nav-item">
<a class="nav-link text-dark" asp-controller="Account" asp-action="Logout">ログアウト</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-controller="Account" asp-action="Login">ログアウトせずログインへ</a>
</li>
@* ここまで追加 *@
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2024 - SessionTimeoutExtensionIisMvc - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Перевірка роботи налагодженням
Перевірте поведінку входу та переконайтеся, що ви не можете отримати доступ до інших сторінок, якщо ви не ввійшли в систему. Час тайм-ауту при налагодженні локально відрізняється від часу запуску на IIS, тому вам не потрібно перевіряти тайм-аут при налагодженні.
Розгортання програм в IIS
Налаштуйте службу IIS і розгорніть створену програму. Налаштування IIS тут не розглядається, оскільки це було б зайвим. Детальні пояснення дивіться в розділі Поради та іншу інформацію нижче.
- Настроювання інформаційних служб Інтернету (IIS) на Windows Server
- Віддалена публікація програм ASP.NET Core на веб-сервері IIS в Інтернеті
Перевірка тайм-аутів сеансів для програм, розгорнутих у службах IIS
Поки що ні програма, ні IIS не встановили тайм-аут сеансу, тому все має бути за замовчуванням.
Спочатку авторизуйтесь і залиште його принаймні на 20 хвилин, а потім Index
спробуйте отримати доступ до сторінки and Privacy
.
Ви можете отримати доступ до нього протягом 20 хвилин, але якщо час очікування минув, ви повинні перейти на сторінку входу.
Подовження періоду очікування в диспетчері IIS
Якщо ви не встановили час очікування у вашій програмі, ви зможете подовжити час очікування у налаштуваннях пулу програм.
Під час запуску диспетчера IIS виберіть пункт Пули програм у меню ліворуч. Виберіть пул програм, який ви використовуєте для керування сайтом, а потім виберіть Додаткові налаштування в меню праворуч.
У групі Модель процесу є пункт під назвою Час очікування простою (хвилини), тому встановіть для нього будь-яку кількість часу в хвилинах.
Після налаштування підтвердіть натисканням кнопки OK.
Після цього, будь ласка, перевірте роботу тайм-ауту сесії. Якщо ви встановите значення 60 хвилин, це буде успішно, якщо ви не вийшли з системи, навіть якщо сторінка перейде через 50 хвилин після входу.
Хоча деякі люди можуть захотіти максимально збільшити час між тайм-аутами сеансу, максимум, який можна встановити, становить 29 годин. Це відповідає часу, коли пул додатків переробляється. Ви також можете подовжити час очікування, збільшивши час переробки пулу програм. Існує також занепокоєння, що використана пам'ять і надалі залишатиметься, тому, якщо ви її зміните, відрегулюйте її відповідно до операції.
Крім того, навіть якщо ви встановили велику кількість часу до тайм-ауту, він часто закінчується до цього. Це пов'язано з тим, що пул програм переробляється до закінчення тайм-ауту, або він переходить у призупинений стан, якщо до нього не зверталися протягом деякого часу. Якщо ви хочете переконатися в тому, що сеанс зберігається протягом зазначеної кількості часу, потрібно вжити інших заходів.