Aumentando o tempo limite da sessão para aplicativos ASP.NET Core em execução no IIS

Página atualizada :
Data de criação de página :

Ambiente de verificação de operação

Estúdio Visual
  • Visual Studio 2022
ASP.NET Núcleo
  • 8 (Páginas da navalha, MVC)
Servidor Windows
  • 2022
IIS
  • 10.0

Ambiente operacional

Não o testamos em todos os casos, mas deve funcionar geralmente nas seguintes condições:

Estúdio Visual
  • Qualquer coisa que possa desenvolver um projeto ASP.NET Core
ASP.NET Núcleo
  • Qualquer versão (MVC, Razor Pages, API)
Servidor Windows
  • Windows Server 2008 ou posterior
IIS
  • 7.0 ou posterior

Inicialmente

Os aplicativos escritos com o ASP.NET Core são executados em um servidor simples chamado "Kestrel", e a duração, como o tempo limite da sessão, é definida principalmente programaticamente.

No entanto, se você estiver hospedando no IIS e executando um aplicativo ASP.NET Core, convém substituir o valor definido no IIS sobre a configuração programática.

O tempo limite da sessão devido ao tempo ocioso descrito esse tempo é afetado pelas configurações do IIS, então vamos definir esta. No entanto, observe que o tempo da sessão é afetado por várias configurações, portanto, dependendo da configuração, o tempo limite da sessão pode não ser estendido apenas com essa configuração.

Isso é explicado com um programa de teste, mas se você quiser uma maneira rápida de configurá-lo, pule para o final desta página.

Programas de Login

Desta vez, vamos verificar o funcionamento na sessão de login. Você pode usar outras sessões, mas esteja ciente do tempo limite padrão para sessões.

Para programas de login, as dicas a seguir são quase sempre usadas, portanto, consulte o seguinte para obter detalhes. Não importa se é Razor Pages ou MVC.

Esta dica é apenas sobre tempos limite de sessão, então não vou entrar no resto do código. Modifique o namespace para corresponder ao seu projeto.

Código do projeto 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();

Páginas/Conta/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);
    }
  }
}

Páginas/Conta/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"); }
}

Páginas/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>

Páginas/Compartilhado/_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">
      &copy; 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>

Código do projeto 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();

Modelos/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; } = "";
  }
}

Controladores/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));
    }
  }
}

Visualizações/Conta/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"); }
}

Visualizações/Home/Index.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>

Visualizações/Compartilhado/_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">
      &copy; 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>

Verificando a operação por depuração

Verifique o comportamento de início de sessão e certifique-se de que não consegue aceder a outras páginas se não tiver sessão iniciada. O tempo limite ao depurar localmente é diferente do tempo limite ao executar no IIS, portanto, você não precisa verificar o tempo limite ao depurar.

Implantando programas no IIS

Configure o IIS e implante o programa que você criou. A configuração do IIS não é abordada aqui porque seria redundante. Para obter explicações detalhadas, consulte Dicas e outras informações abaixo.

Verificando tempos limite de sessão para aplicativos implantados no IIS

Até agora, nem o programa nem o IIS definiram o tempo limite da sessão, então tudo deve ser padrão. Primeiro, faça login e deixe por pelo menos 20 minutos e, em seguida Index , tente acessar a página e Privacy . Você pode acessá-lo por até 20 minutos, mas se ele expirar, você deve ser levado para a página de login.

Estender o período de tempo limite no Gerenciador do IIS

Se você não tiver definido um tempo limite em seu programa, poderá estender o tempo limite nas configurações do pool de aplicativos.

Ao iniciar o Gerenciador do IIS, selecione Pools de Aplicativos no menu à esquerda. Selecione o pool de aplicativos que você está usando para operar seu site e, em seguida, selecione Configurações avançadas no menu à direita.

No grupo Modelo de Processo, há um item chamado Tempo Limite de Ociosidade (minutos), portanto, defina-o para qualquer quantidade de tempo em minutos.

Após a configuração, confirme com o botão OK.

Depois disso, verifique o funcionamento do tempo limite da sessão. Se você defini-lo para 60 minutos, ele será bem-sucedido se você não estiver desconectado, mesmo se a transição de página 50 minutos após o login.

Embora algumas pessoas possam querer estender a quantidade de tempo entre os tempos limite da sessão tanto quanto possível, o máximo que pode ser definido é 29 horas. Isso corresponde ao momento em que o pool de aplicativos é reciclado. Você também pode estender o tempo limite estendendo o tempo de reciclagem do pool de aplicativos. Há também uma preocupação de que a memória usada continuará a permanecer, por isso, se você alterá-la, por favor, ajustá-la de acordo com a operação.

Além disso, mesmo que você defina uma grande quantidade de tempo antes do tempo limite, ele geralmente expira antes disso. Isso ocorre porque o pool de aplicativos é reciclado antes que o tempo limite expire ou ele entra em um estado pausado se não tiver sido acessado por um tempo. Se você quiser garantir que a sessão seja mantida por um período de tempo especificado, será necessário tomar outras medidas.