การเพิ่มการหมดเวลาเซสชันสําหรับโปรแกรมประยุกต์หลัก ASP.NET ที่ทํางานบน IIS

ปรับปรุงหน้า :
วันที่สร้างเพจ :

สภาพแวดล้อมการตรวจสอบการทํางาน

วิชวลสตูดิโอ
  • วิชวลสตูดิโอ 2022
แกน ASP.NET
  • 8 (หน้ามีดโกน, MVC)
เซิร์ฟเวอร์ Windows
  • 2022
ไอเอส
  • 10.0

สภาพแวดล้อมในการทํางาน

เราไม่ได้ทดสอบในทุกกรณี แต่ควรทํางานโดยทั่วไปภายใต้เงื่อนไขต่อไปนี้:

วิชวลสตูดิโอ
  • อะไรก็ได้ที่สามารถพัฒนาโครงการ ASP.NET Core ได้
แกน ASP.NET
  • เวอร์ชันใดก็ได้ (MVC, Razor Pages, API)
เซิร์ฟเวอร์ Windows
  • 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"); }
}

หน้า/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>

หน้า/แชร์/_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>

รหัสโครงการ 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"); }
}

มุมมอง/หน้าแรก/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>

มุมมอง/แชร์/_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>

การตรวจสอบการทํางานโดยการดีบัก

ตรวจสอบลักษณะการลงชื่อเข้าใช้และตรวจสอบให้แน่ใจว่าคุณไม่สามารถเข้าถึงหน้าอื่นๆ ได้หากคุณไม่ได้เข้าสู่ระบบ เวลาหมดเวลาเมื่อการดีบักในเครื่องจะแตกต่างจากเมื่อทํางานบน IIS ดังนั้นคุณไม่จําเป็นต้องตรวจสอบการหมดเวลาเมื่อทําการดีบัก

การปรับใช้โปรแกรมกับ IIS

ตั้งค่า IIS และปรับใช้โปรแกรมที่คุณสร้างขึ้น การตั้งค่า IIS ไม่ครอบคลุมที่นี่เนื่องจากจะซ้ําซ้อน สําหรับคําอธิบายโดยละเอียด โปรดดูเคล็ดลับและข้อมูลอื่นๆ ด้านล่าง

การตรวจสอบการหมดเวลาของเซสชันสําหรับโปรแกรมประยุกต์ที่ปรับใช้บน IIS

จนถึงตอนนี้ ทั้งโปรแกรมและ IIS ยังไม่ได้ตั้งค่าการหมดเวลาของเซสชัน ดังนั้นทุกอย่างควรเป็นค่าเริ่มต้น ขั้นแรก ให้เข้าสู่ระบบและปล่อยทิ้งไว้อย่างน้อย 20 นาที จากนั้น Index ลองเข้าถึงหน้า และ Privacy . คุณสามารถเข้าถึงได้นานถึง 20 นาที แต่ถ้าหมดเวลา คุณควรเข้าสู่หน้าเข้าสู่ระบบ

ขยายระยะเวลาการหมดเวลาในตัวจัดการ IIS

หากคุณไม่ได้ตั้งค่าการหมดเวลาในโปรแกรมของคุณ คุณควรจะสามารถขยายเวลาหมดเวลาในการตั้งค่าพูลแอปพลิเคชันของคุณได้

เมื่อคุณเริ่มตัวจัดการ IIS เลือกพูลโปรแกรมประยุกต์จากเมนูทางด้านซ้าย เลือกพูลแอปพลิเคชันที่คุณใช้เพื่อใช้งานไซต์ของคุณ จากนั้นเลือก การตั้งค่าขั้นสูง จากเมนูทางด้านขวา

ในกลุ่ม ตัวแบบกระบวนการ มีรายการที่เรียกว่า Idle Timeout (นาที) ดังนั้นให้ตั้งค่าเป็นจํานวนครั้งใดก็ได้ในหน่วยนาที

หลังจากตั้งค่าแล้ว ให้ยืนยันด้วยปุ่ม OK

หลังจากนั้น โปรดตรวจสอบการทํางานของการหมดเวลาเซสชัน หากคุณตั้งค่าเป็น 60 นาที จะสําเร็จหากคุณไม่ได้ออกจากระบบแม้ว่าหน้าจะเปลี่ยน 50 นาทีหลังจากเข้าสู่ระบบ

แม้ว่าบางคนอาจต้องการขยายระยะเวลาระหว่างการหมดเวลาของเซสชันให้มากที่สุด แต่ค่าสูงสุดที่สามารถตั้งค่าได้คือ 29 ชั่วโมง ซึ่งสอดคล้องกับเวลาที่พูลแอปพลิเคชันถูกรีไซเคิล คุณยังสามารถขยายเวลาหมดเวลาได้โดยขยายเวลารีไซเคิลของพูลแอปพลิเคชัน นอกจากนี้ยังมีข้อกังวลว่าหน่วยความจําที่ใช้แล้วจะยังคงหลงเหลืออยู่ดังนั้นหากคุณเปลี่ยนโปรดปรับเปลี่ยนตามการทํางาน

นอกจากนี้ แม้ว่าคุณจะตั้งเวลาจํานวนมากก่อนหมดเวลา แต่ก็มักจะหมดเวลาก่อนหน้านั้น นี่เป็นเพราะพูลแอปพลิเคชันถูกรีไซเคิลก่อนที่การหมดเวลาจะหมดอายุ หรือเข้าสู่สถานะหยุดชั่วคราวหากไม่มีการเข้าถึงชั่วขณะหนึ่ง หากคุณต้องการให้แน่ใจว่าเซสชันได้รับการดูแลตามระยะเวลาที่กําหนดคุณต้องใช้มาตรการอื่น ๆ