Razor Pagesでシンプルな認証機能を持つアプリケーションを作成する - ASP.NET Core

Razor Pagesでシンプルな認証機能を持つアプリケーションを作成する手順とコードを紹介します。

概要

ユーザーIDやパスワードで認証してWebアプリケーションを利用させたい場合があります。 この記事では、ASP.NET Coreの認証機能を利用した非常にシンプルな認証機能のあるアプリケーションを作成する手順と コードを紹介します。

プログラム例

ASP.NET Core アプリケーションを作成します。アプリケーションのフレームワークは .NET 6を利用します。

以下のファイル構成とします。

コード

下記ファイル、コードを作成します。
Index.cshtml
@page
@model AuthenticationSimple.Pages.IndexModel
@{
}
<html>
  <head>
    <title>index</title>
  </head>

  <body>
    <h1>index</h1>
    <a href="Login">Login</a><br/>
    <a href="Logout">Logout</a><br/>
    <a href="Content">Content</a><br/>
  </body>

</html>
Index.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace AuthenticationSimple.Pages
{
  public class IndexModel : PageModel
  {
    public void OnGet()
    {
    }
  }
}
Login.cshtml
@page
@model AuthenticationSimple.Pages.LoginModel
@{
}
<html>
  <head>

  </head>
  <body>
    <h1>Login</h1>
    <p>ログインしました。</p>
  </body>
</html>
Login.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace AuthenticationSimple.Pages
{
  public class LoginModel : PageModel
  {
    public void OnGet()
    {
      Claim[] claims = {
        new Claim(ClaimTypes.NameIdentifier, "User"),
        new Claim(ClaimTypes.Name, "ユーザー"),
      };

      ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

      HttpContext.SignInAsync(
        CookieAuthenticationDefaults.AuthenticationScheme,
        new ClaimsPrincipal(claimsIdentity),
        new AuthenticationProperties {
          IsPersistent = true
        }
      );
    }

  }
}
Logout.cshtml
@page
@model AuthenticationSimple.Pages.LogoutModel
@{
}
<html>
  <head>

  </head>
  <body>
    <h1>Logout</h1>
    <p>ログアウトしました。</p>
  </body>
</html>
Logout.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace AuthenticationSimple.Pages
{
  public class LogoutModel : PageModel
  {
    public void OnGet()
    {
      HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }
  }
}
Content.cshtml
@page
@model AuthenticationSimple.Pages.ContentModel
@{
}
<html>
  <head>

  </head>
  <body>
    <h1>コンテンツのページ</h1>
    <p>ユーザー:@Model.OutUserName</p>
    <p>コンテンツのページです</p>
    <p>認証が必要なページです</p>
  </body>
</html>
Content.cshtml.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;

namespace AuthenticationSimple.Pages
{
  [Authorize]
  public class ContentModel : PageModel
  {
    public string OutUserName { get; set; }

    public void OnGet()
    {
      foreach (Claim c in User.Claims) {
        if (c.Type == ClaimTypes.Name) OutUserName = c.Value;
      }
    }
  }
}

Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
WebApplication app = builder.Build();

app.UseRouting(); 

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

app.MapRazorPages();
app.Run();

解説

Content.cshtml, Content.cshtml.cs

/Content が認証が必要なページです。
ページモデルクラスに [Authorize] 属性を記述すると、認証が必要なページとして動作します。
  [Authorize]
  public class ContentModel : PageModel
  {
    /* 中略 */
  }

ページ内に現在ログインしているユーザーの名前を表示するエリアを作成しています。OutUserNameプロパティにユーザーの名称を設定する動作にします。~
  <body>
    <h1>コンテンツのページ</h1>
    <p>ユーザー:@Model.OutUserName</p>
    <p>コンテンツのページです</p>
    <p>認証が必要なページです</p>
  </body>

ログインしているユーザーの情報は、ページモデルクラスの User プロパティの Claims オブジェクトに設定されています。 Claimsオブジェクト内の値は、Login.cshtml.csで設定したClaimオブジェクトの値が代入されています。 今回はユーザー名を取得してページに表示するため、Claims オブジェクトをループで走査し、TypeプロパティがClaimTypes.Name である Claimオブジェクトの値を取得してユーザー名として画面に表示します。
  public void OnGet()
  {
    foreach (Claim c in User.Claims) {
      if (c.Type == ClaimTypes.Name) OutUserName = c.Value;
    }
  }

Login.cshtml, Login.cshtml.cs

ログインページになります。今回はIDやパスワードの入力なしに、ログインページにアクセスすると ログイン済みとなる動作にします。
claims 変数にログインするユーザーの情報を設定します。今回は、NameIdentifierに"User"、Nameに"ユーザー"の値を設定します。 ログイン後にClaimの値を取得して、ログインしているユーザーの情報をページに表示したりできます。
claims から、ClaimsIdentityオブジェクトを作成します。
ログインできたことにするには、HttpContext.SignInAsync() メソッドを呼び出します。 第一引数に、認証スキーマ、第二引数に ClaimsPrincipalオブジェクト、第三引数に AuthenticationProperties オブジェクトを与えます。
public void OnGet()
{
  Claim[] claims = {
    new Claim(ClaimTypes.NameIdentifier, "User"),
    new Claim(ClaimTypes.Name, "ユーザー"),
  };

  ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

  HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties {
      IsPersistent = true
    }
  );
}

Logout.cshtml, Logout.cshtml.cs

Logout ページにアクセスするとログアウトとします。ログアウトする場合は、HttpContext.SignOutAsync() メソッドを呼び出します。
public void OnGet()
{
  HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}

Program.cs

Program.csは通常のRazorPagesの初期化処理に加えて、builder.Services.AddAuthentication を追加します。 また、UseCookiePolicy() UseAuthentication() UseAuthorization() を追加します。
using Microsoft.AspNetCore.Authentication.Cookies;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
WebApplication app = builder.Build();

app.UseRouting(); 

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

app.MapRazorPages();
app.Run();

実行結果

プロジェクトを実行し、アプリケーションルートのURLにアクセスします。下図のページが表示されます。
はじめに、ログインしていない状態で、ログインが必要なページにアクセスしてみます。[Content]のリンクをクリックします。


ページは表示されず、(アプリケーションルートURL)/Account/Login?ReturnUrl=%2FContent にリダイレクトされます。 アクセスしたページのURIがReturnUrlパラメーターにセットされ、/Account/Login にリダイレクトされる動作になっています。


アプリケーションルートのページに戻ります。ログインしてみます。[Login]のリンクをクリックします。


Loginページが表示され、ログインできました。


再度アプリケーションルートのURLに戻り、[Content]のリンクをクリックします。 ログイン状態になったため、ページの内容が表示されます。 ページ内に現在ログインしているユーザー名「ユーザー」の文字列も表示できています。


アプリケーションルートのページに戻り[Logout]リンクをクリックします。下図のページが表示され、ログアウトの処理が実行されます。


アプリケーションルートのページに戻り、再度[Content]のリンクをクリックします。ログアウト状態になっているため、 (アプリケーションルートURL)/Account/Login?ReturnUrl=%2FContent にリダイレクトされます。

補足

Program.csで UseAuthorization()UseRouting() より後ろに記述する必要があります。
Program.csのコードが以下の場合、app.UseAuthorization()app.UseRouting()より手前にあるため、認証が必要なページに アクセスするとエラーが発生します。
Program.cs (エラーになる例)
using Microsoft.AspNetCore.Authentication.Cookies;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
WebApplication app = builder.Build();


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

app.UseRouting(); 
app.MapRazorPages();
app.Run();

プロジェクトを実行し、Contentページを表示すると以下のエラーが発生します。


エラーメッセージ
An unhandled exception occurred while processing the request.

InvalidOperationException: Endpoint /Content contains authorization metadata, but a middleware was not found that supports authorization. Configure your application startup by adding app.UseAuthorization() in the application startup code. If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAuthorization() must go between them.

UseRouting()より後ろにUseAuthorization()が記述されていれば、エラーは発生しません。
using Microsoft.AspNetCore.Authentication.Cookies;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
WebApplication app = builder.Build();


app.UseCookiePolicy();
app.UseAuthentication();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();
app.Run();

次の手順

Webアプリケーション独自のアカウントでの認証画面を作成する場合は次の記事を参照してください。

Azure ADを利用した認証を実装する場合は次の記事を参照してください。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-06-16
作成日: 2022-08-03
iPentec all rights reserverd.