.NET 6 實作 JWT (使用 Minimal API)

April 28, 2025

這篇文章會介紹 JWT 的基本知識,以及用 .NET 6 的 Minimal API 打造一個簡易的程式範例。

基本知識

  • 全名是 JSON Web Token,顧名思義是將安全資訊存為 JSON 物件。
  • JWT 是由後端產生,用於驗證身分的 Token,產生後會交給前端存放。
  • 分成 Header, Payload, Signature 三個部分,Header 存放通用資訊 (例如使用的演算法)、Payload 存放使用者資訊 (例如用戶帳號)、Signature 存放數位簽名。
  • 透過 Base64 編碼 Header 和 Payload,所以建議存放非機密資訊,因為可以反向解碼。
  • 可以由「對稱加密」或「非對稱加密」實作數位簽名功能。
    • 對稱加密:用同一把公鑰加密、解密資料。
    • 非對稱加密:用私鑰加密的資料,只能用公鑰來解密。

使用對稱加密的 .NET 6 範例

以下是一個最小可行的 C# 專案(使用 .NET 6/7 Minimal API)用來測試 JWT Token 的產生與驗證。這個範例會包含:

  1. 產生 JWT Token 的 API (/login)
  2. 一個需要驗證 JWT 的 API (/secure)
  3. 使用內建 JWT 驗證中介軟體

建立新資料夾,接著開啟 PowerShell,輸入指令建立 ASP.NET Core 專案,並安裝必要的套件:

dotnet new web
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

接著建立 Program.cs :

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// JWT 設定
var jwtKey = "1234567890asfdghjklmnbvcxzqwertyuiop[]!@"; // 公鑰內文,請改成更安全的字串
var issuer = "MyApp";

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // 設定驗證合法有效的 Token
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = false,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = issuer,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)) // 建立對稱式的公鑰
        };
    });
builder.Services.AddAuthorization();

var app = builder.Build();

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

// 產生 Token 的 Login API
app.MapPost("/login", (UserLogin user) =>
{
    if (user.Username == "test" && user.Password == "1234") // 這裡是模擬登入成功
    {
        var claims = new[]
        {
            new Claim(ClaimTypes.Name, user.Username)
        };

        var token = new JwtSecurityToken( // 產生 JWT
            issuer: issuer,
            claims: claims,
            expires: DateTime.UtcNow.AddMinutes(1), // 逾期時間,暫時設定為 1 分鐘
            signingCredentials: new SigningCredentials(
                new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
                SecurityAlgorithms.HmacSha256)
        );

        var tokenString = new JwtSecurityTokenHandler().WriteToken(token);

        return Results.Ok(new { token = tokenString });
    }

    return Results.Unauthorized();
});

// 受保護的 API
app.MapGet("/secure", (ClaimsPrincipal user) =>
{
    return $"Hello {user.Identity?.Name}, you accessed a protected endpoint!";
}).RequireAuthorization();

app.Run();

// 使用者登入資料紀錄 (可參考 https://www.huanlintalk.com/2022/03/csharp-9-record-explained.html)
record UserLogin(string Username, string Password);

接著在命令列輸入以下指令啟動程式:

dotnet run

然後可以在 Postman 或類似軟體發送 POST 請求到 Login API:

{
  "username": "test",
  "password": "1234"
}

就可以得到回傳的 JWT。

呼叫登入 API 成功

接著再建立新的 GET 請求,驗證方式選擇 Bearer,並輸入剛剛的 JWT,發送後就能得到驗證成功的結果囉。 

呼叫受保護的 API 成功

如果輸入錯誤或逾時的話,會得到 401 未認證的回應:

呼叫受保護的 API 逾時

Bonus: JWT Token 沒有立刻失效

如果把逾期時間設得很短的話,可能會發現 Token 沒有立刻失效。這是因為 Clock Skew (時鐘偏移) 的設定,有著 5 分鐘的容許值,以接受不同伺服器略有差異的時間。如果不需要容許值的話,可以調整 TokenValidationParameters  的 ClockSkew 為 TimeSpan.Zero 。

參考資料:c# - JWT token expiration not working in Asp.Net Core API? - Stack Overflow

參考資料:其它 ASP.NET API 加入 JWT 驗證

想用 MVC API 或其它 ASP.NET Core API 版本加入 JWT,可以參考以下連結:

參考資料:古古的「JWT Token 原理」系列文

Note: 這系列文章的數位簽名是採用「非對稱加密」。