C#でのJSON Web Token (JWT) の実装方法:完全ガイド

C#でJSON Web Token (JWT)を実装することで、アプリケーションの認証とセキュリティを強化できます。本記事では、JWTの基本概念から始め、C#環境での実装方法、実際の使用例や応用例までを詳しく解説します。これを通じて、安全で信頼性の高いアプリケーションを構築するための知識を習得しましょう。

目次

JWTとは何か

JWT(JSON Web Token)は、JSON形式のデータを安全に転送するためのコンパクトでURLセーフなトークンです。主に認証情報の伝達や、情報の検証・認証に使用されます。JWTは3つの部分(ヘッダー、ペイロード、署名)から構成され、それぞれがドットで区切られた文字列として表現されます。

JWTの用途

JWTは、以下のような用途で広く使われています。

認証

ユーザーがログインすると、サーバーはJWTを生成し、それをクライアントに返します。クライアントはその後のリクエストでJWTを使用し、サーバーはJWTを検証することでユーザーを認証します。

情報の安全な伝達

JWTは署名されているため、改ざんされていないことを確認できます。これにより、信頼性のある情報の伝達が可能になります。

JWTの仕組み

JWTは、ヘッダー、ペイロード、署名の3つの部分から構成されています。これらの部分はそれぞれエンコードされ、ドットで区切られて1つのトークンとして表現されます。

ヘッダー

ヘッダーには、トークンのタイプ(JWT)と使用する署名アルゴリズム(例:HMAC SHA256)が含まれます。

{
  "alg": "HS256",
  "typ": "JWT"
}

ペイロード

ペイロードには、トークンの対象者、発行者、期限などのクレームと呼ばれる情報が含まれます。クレームには、登録済みクレーム、公開クレーム、非公開クレームがあります。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

登録済みクレーム

トークンの有効期限(exp)や発行者(iss)など、あらかじめ定義されたクレームです。

公開クレーム

ユーザー定義の情報で、登録済みクレーム以外のデータを含めることができます。

非公開クレーム

アプリケーション間で共有される情報で、共通の合意に基づくデータです。

署名

署名は、ヘッダーとペイロードを組み合わせ、秘密鍵または公開鍵を使って生成されます。これにより、トークンの内容が改ざんされていないことを確認できます。

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret)

トークンの例

以下は、エンコードされたJWTの例です。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

C#環境の設定

C#でJWTを実装するためには、開発環境の設定が必要です。ここでは、Visual Studioを使用してC#プロジェクトを設定する方法を説明します。

Visual Studioのインストール

最新のVisual Studioをインストールします。公式サイトからダウンロードし、インストーラーの指示に従ってセットアップを行います。

新しいプロジェクトの作成

Visual Studioを起動し、以下の手順で新しいC#プロジェクトを作成します。

  1. 「新しいプロジェクトの作成」をクリック
  2. 「C#」を選択し、「コンソールアプリ」または「ASP.NET Core Webアプリ」を選択
  3. プロジェクト名と保存場所を指定して「作成」をクリック

必要なパッケージのインストール

JWTの処理を行うために、必要なNuGetパッケージをインストールします。ここでは、人気のあるライブラリ System.IdentityModel.Tokens.Jwt を使用します。

  1. 「ツール」メニューから「NuGet パッケージマネージャー」を選択し、「パッケージマネージャーコンソール」を開く
  2. 以下のコマンドを実行してパッケージをインストールします。
   Install-Package System.IdentityModel.Tokens.Jwt

プロジェクトの構成

プロジェクトの構成を確認し、必要に応じて appsettings.jsonProgram.cs に設定を追加します。特に、JWTのシークレットキーや発行者情報などの設定を行います。

{
  "Jwt": {
    "Key": "your_secret_key",
    "Issuer": "your_issuer",
    "Audience": "your_audience",
    "ExpiryMinutes": 60
  }
}

これで、C#プロジェクトの基本設定が完了し、JWTを実装する準備が整いました。

JWTライブラリのインストール

JWTをC#で扱うために、必要なライブラリをインストールします。ここでは、System.IdentityModel.Tokens.Jwtパッケージを使用します。このライブラリは、JWTの生成、署名、検証を簡単に行うための機能を提供します。

NuGetパッケージのインストール

NuGetパッケージマネージャーを使って、System.IdentityModel.Tokens.Jwtをインストールします。以下の手順で進めます。

手順1: パッケージマネージャーコンソールを開く

Visual Studioの上部メニューから「ツール」→「NuGet パッケージマネージャー」→「パッケージマネージャーコンソール」を選択します。

手順2: パッケージのインストールコマンドを実行

パッケージマネージャーコンソールに以下のコマンドを入力して、パッケージをインストールします。

Install-Package System.IdentityModel.Tokens.Jwt

プロジェクトファイルの確認

パッケージがインストールされると、プロジェクトファイル(.csproj)に以下のようなエントリが追加されます。

<ItemGroup>
  <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.8.0" />
</ItemGroup>

ライブラリのインポート

プロジェクト内でJWT機能を使用するために、必要な名前空間をインポートします。例えば、Program.csや他のC#ファイルの先頭に以下のコードを追加します。

using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Text;

これで、JWTを扱うためのライブラリのインストールと基本設定が完了しました。次に、具体的なJWTの生成方法を見ていきましょう。

JWTの生成方法

ここでは、C#を使ってJSON Web Token (JWT) を生成する方法を詳しく解説します。具体的なコード例を通じて、JWTの生成プロセスを理解しましょう。

JWT生成の基本手順

JWTを生成するためには、以下の手順を踏みます。

  1. 秘密鍵の設定
  2. セキュリティトークンの記述
  3. トークンの生成

手順1: 秘密鍵の設定

まず、JWTの署名に使用する秘密鍵を設定します。これはトークンの改ざんを防ぐために重要です。

var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key"));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

手順2: セキュリティトークンの記述

次に、トークンに含めるクレーム(ユーザー情報など)や有効期限などを設定します。

var claims = new[] {
    new Claim(JwtRegisteredClaimNames.Sub, "1234567890"),
    new Claim(JwtRegisteredClaimNames.Name, "John Doe"),
    new Claim(JwtRegisteredClaimNames.Email, "john.doe@example.com"),
    new Claim("role", "admin")
};

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(claims),
    Expires = DateTime.UtcNow.AddMinutes(60),
    SigningCredentials = credentials,
    Issuer = "your_issuer",
    Audience = "your_audience"
};

手順3: トークンの生成

最後に、トークンを生成します。JwtSecurityTokenHandler クラスを使用してトークンを作成し、シリアライズします。

var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);

Console.WriteLine("Generated JWT: " + tokenString);

完全なコード例

以下に、上記の手順をすべて含んだ完全なコード例を示します。

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

class Program
{
    static void Main()
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key"));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var claims = new[] {
            new Claim(JwtRegisteredClaimNames.Sub, "1234567890"),
            new Claim(JwtRegisteredClaimNames.Name, "John Doe"),
            new Claim(JwtRegisteredClaimNames.Email, "john.doe@example.com"),
            new Claim("role", "admin")
        };

        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(claims),
            Expires = DateTime.UtcNow.AddMinutes(60),
            SigningCredentials = credentials,
            Issuer = "your_issuer",
            Audience = "your_audience"
        };

        var tokenHandler = new JwtSecurityTokenHandler();
        var token = tokenHandler.CreateToken(tokenDescriptor);
        var tokenString = tokenHandler.WriteToken(token);

        Console.WriteLine("Generated JWT: " + tokenString);
    }
}

このコードを実行すると、指定された情報に基づいてJWTが生成され、そのトークンがコンソールに出力されます。これで、C#でのJWT生成方法が理解できたと思います。次に、JWTの検証方法を見ていきましょう。

JWTの検証方法

JWTの検証は、トークンの正当性と有効性を確認するために重要です。ここでは、C#を使ってJWTを検証する方法を詳しく説明します。

JWT検証の基本手順

JWTを検証するためには、以下の手順を踏みます。

  1. 検証に必要なキーとパラメータの設定
  2. トークンの検証プロセスの実行

手順1: 検証に必要なキーとパラメータの設定

まず、トークンの署名を検証するための秘密鍵と、トークンの検証パラメータを設定します。

var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key"));
var tokenValidationParameters = new TokenValidationParameters
{
    ValidateIssuer = true,
    ValidateAudience = true,
    ValidateLifetime = true,
    ValidateIssuerSigningKey = true,
    ValidIssuer = "your_issuer",
    ValidAudience = "your_audience",
    IssuerSigningKey = securityKey
};

手順2: トークンの検証プロセスの実行

次に、JWTを検証します。JwtSecurityTokenHandlerクラスを使用してトークンを検証し、検証結果を取得します。

var tokenHandler = new JwtSecurityTokenHandler();
try
{
    var principal = tokenHandler.ValidateToken(tokenString, tokenValidationParameters, out SecurityToken validatedToken);
    Console.WriteLine("Token is valid. Claims:");
    foreach (var claim in principal.Claims)
    {
        Console.WriteLine($"{claim.Type}: {claim.Value}");
    }
}
catch (SecurityTokenException ex)
{
    Console.WriteLine("Token validation failed: " + ex.Message);
}

完全なコード例

以下に、上記の手順をすべて含んだ完全なコード例を示します。

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;

class Program
{
    static void Main()
    {
        var tokenString = "your_generated_jwt_token";

        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your_secret_key"));
        var tokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = "your_issuer",
            ValidAudience = "your_audience",
            IssuerSigningKey = securityKey
        };

        var tokenHandler = new JwtSecurityTokenHandler();
        try
        {
            var principal = tokenHandler.ValidateToken(tokenString, tokenValidationParameters, out SecurityToken validatedToken);
            Console.WriteLine("Token is valid. Claims:");
            foreach (var claim in principal.Claims)
            {
                Console.WriteLine($"{claim.Type}: {claim.Value}");
            }
        }
        catch (SecurityTokenException ex)
        {
            Console.WriteLine("Token validation failed: " + ex.Message);
        }
    }
}

このコードを実行すると、指定されたJWTの検証が行われ、トークンが有効であればクレーム情報が表示されます。無効な場合はエラーメッセージが出力されます。これで、C#でのJWT検証方法が理解できたと思います。次に、JWTの実際の使用例を見ていきましょう。

JWTの使用例

ここでは、実際のアプリケーションでJWTを使用する方法について具体例を交えて説明します。Web APIにおけるJWT認証の実装例を通じて、実践的な利用方法を学びます。

Web APIの設定

まず、ASP.NET Coreを使用してWeb APIプロジェクトを作成し、JWT認証を設定します。

プロジェクトの作成

Visual Studioで新しいASP.NET Core Web APIプロジェクトを作成します。プロジェクトテンプレートから「ASP.NET Core Web API」を選択し、プロジェクトを作成します。

必要なパッケージのインストール

JWTを使用するために必要なNuGetパッケージをインストールします。

Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

JWT認証の設定

Startup.csファイルにJWT認証の設定を追加します。

public void ConfigureServices(IServiceCollection services)
{
    var key = Encoding.ASCII.GetBytes("your_secret_key");

    services.AddAuthentication(x =>
    {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(x =>
    {
        x.RequireHttpsMetadata = false;
        x.SaveToken = true;
        x.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(key),
            ValidateIssuer = true,
            ValidIssuer = "your_issuer",
            ValidateAudience = true,
            ValidAudience = "your_audience",
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
    });

    services.AddControllers();
}

コントローラーの作成

JWT認証を必要とするAPIコントローラーを作成します。以下は、認証が必要なエンドポイントの例です。

[Authorize]
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok(new { message = "JWT認証が成功しました!" });
    }
}

トークンの取得エンドポイント

ユーザーが認証に成功した場合にJWTを発行するためのエンドポイントを作成します。

[AllowAnonymous]
[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] UserLogin model)
    {
        // ユーザーの認証処理を行い、成功した場合にトークンを発行します
        if (model.Username == "test" && model.Password == "password")
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes("your_secret_key");
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, model.Username)
                }),
                Expires = DateTime.UtcNow.AddHours(1),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
                Issuer = "your_issuer",
                Audience = "your_audience"
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            var tokenString = tokenHandler.WriteToken(token);

            return Ok(new { Token = tokenString });
        }

        return Unauthorized();
    }
}

完全な使用例のコード

以下に、全ての設定とエンドポイントを含んだ完全なコード例を示します。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        var key = Encoding.ASCII.GetBytes("your_secret_key");

        services.AddAuthentication(x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddJwtBearer(x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidateIssuer = true,
                ValidIssuer = "your_issuer",
                ValidateAudience = true,
                ValidAudience = "your_audience",
                ValidateLifetime = true,
                ClockSkew = TimeSpan.Zero
            };
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

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

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

[Authorize]
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    [HttpGet]
    public IActionResult Get()
    {
        return Ok(new { message = "JWT認証が成功しました!" });
    }
}

[AllowAnonymous]
[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] UserLogin model)
    {
        if (model.Username == "test" && model.Password == "password")
        {
            var tokenHandler = new JwtSecurityTokenHandler();
            var key = Encoding.ASCII.GetBytes("your_secret_key");
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new Claim[]
                {
                    new Claim(ClaimTypes.Name, model.Username)
                }),
                Expires = DateTime.UtcNow.AddHours(1),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
                Issuer = "your_issuer",
                Audience = "your_audience"
            };

            var token = tokenHandler.CreateToken(tokenDescriptor);
            var tokenString = tokenHandler.WriteToken(token);

            return Ok(new { Token = tokenString });
        }

        return Unauthorized();
    }
}

public class UserLogin
{
    public string Username { get; set; }
    public string Password { get; set; }
}

このコードを実行すると、ユーザーが認証情報を提供してログインした場合にJWTが発行され、そのトークンを使用して認証されたエンドポイントにアクセスできます。これで、実際のアプリケーションでJWTを使用する方法が理解できたと思います。次に、応用例と演習問題を見ていきましょう。

応用例と演習問題

ここでは、JWTの応用例を紹介し、理解を深めるための演習問題を提供します。これにより、JWTの利用方法をより実践的に学ぶことができます。

応用例

JWTは様々なシナリオで利用できます。以下はそのいくつかの例です。

マイクロサービス間の認証

マイクロサービスアーキテクチャでは、各サービス間の通信を認証するためにJWTを使用します。これにより、各サービスはトークンを検証するだけでリクエストの正当性を確認できます。

シングルサインオン(SSO)

SSOシステムでは、ユーザーが一度ログインすると、他の関連サービスにもアクセスできるようにJWTを使用します。トークンを各サービスに渡すことで、ユーザーは再度ログインする必要がなくなります。

APIゲートウェイの認証

APIゲートウェイを通じて外部クライアントからのリクエストを処理する際にJWTを利用します。ゲートウェイはトークンを検証し、有効であれば内部サービスにリクエストを転送します。

演習問題

以下の演習問題に取り組むことで、JWTの実装と使用に関するスキルを向上させましょう。

演習1: JWTのカスタムクレームを追加する

ユーザーの役割(role)や権限(permissions)をクレームに追加したJWTを生成してください。そのトークンを用いて、特定の権限が必要なAPIエンドポイントを保護するコードを実装してください。

演習2: トークンのリフレッシュ機能の実装

短い有効期限のアクセストークンと長い有効期限のリフレッシュトークンを実装し、アクセストークンが期限切れになった場合にリフレッシュトークンを使用して新しいアクセストークンを発行する機能を作成してください。

演習3: ロールベースのアクセス制御の実装

JWTに含まれるユーザーの役割に基づいて、特定のエンドポイントへのアクセスを制限するロールベースのアクセス制御(RBAC)を実装してください。管理者のみがアクセスできるエンドポイントと、一般ユーザーがアクセスできるエンドポイントを設定します。

応用例のコード例

ここでは、JWTのカスタムクレームを追加する簡単な例を示します。

var claims = new[] {
    new Claim(JwtRegisteredClaimNames.Sub, "1234567890"),
    new Claim(JwtRegisteredClaimNames.Name, "John Doe"),
    new Claim(JwtRegisteredClaimNames.Email, "john.doe@example.com"),
    new Claim("role", "admin"),
    new Claim("permissions", "read,write")
};

var tokenDescriptor = new SecurityTokenDescriptor
{
    Subject = new ClaimsIdentity(claims),
    Expires = DateTime.UtcNow.AddMinutes(60),
    SigningCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature),
    Issuer = "your_issuer",
    Audience = "your_audience"
};

var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
Console.WriteLine("Generated JWT with custom claims: " + tokenString);

このコード例では、rolepermissionsというカスタムクレームを追加しています。このトークンを検証する際に、これらのクレームを使ってアクセス制御を行うことができます。

これらの応用例と演習問題を通じて、JWTの実装方法だけでなく、実際のシステムでの応用方法についても理解を深めることができます。次に、この記事の内容をまとめます。

まとめ

この記事では、C#でのJSON Web Token (JWT) の実装方法について、基本概念から具体的な実装方法、そして応用例や演習問題まで幅広く解説しました。以下に、重要なポイントを振り返ります。

基本概念

JWTは、JSON形式のデータを安全に転送するためのトークンであり、認証や情報の安全な伝達に使用されます。JWTはヘッダー、ペイロード、署名の3つの部分から構成されます。

C#での実装手順

  • 環境の設定: Visual Studioを使ったC#開発環境の構築方法
  • ライブラリのインストール: System.IdentityModel.Tokens.Jwtパッケージのインストール
  • JWTの生成: 秘密鍵の設定、クレームの定義、トークンの生成方法
  • JWTの検証: トークンの署名検証とクレームの確認

実際の使用例

  • Web APIにおけるJWT認証の設定と使用方法
  • マイクロサービス間の認証、シングルサインオン(SSO)、APIゲートウェイの認証などの応用例

演習問題

  • JWTのカスタムクレームの追加
  • トークンのリフレッシュ機能の実装
  • ロールベースのアクセス制御の実装

これらの知識とスキルを活用して、セキュアで信頼性の高い認証システムを構築できるようになるでしょう。この記事が、C#でのJWTの実装に役立つ参考資料となれば幸いです。

コメント

コメントする

目次