JWT認証の実装: .NET Coreでのトークン発行と検証

必要なパッケージ

Microsoft.AspNetCore.Authentication.JwtBearer
System.IdentityModel.Tokens.Jwt

トークンハンドラークラス

public static class TokenManager
{
    public static string GenerateToken(UserTokenInfo payload)
    {
        var config = Configuration.GetSection("SecuritySettings");
        var issuer = config["Issuer"];
        var audience = config["Audience"];
        var key = Encoding.UTF8.GetBytes(config["Key"]);

        var claims = new List<Claim>
        {
            new Claim("user_id", payload.UserId.ToString()),
            new Claim("issued_at", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
            new Claim("valid_from", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString()),
            new Claim("expires_at", DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds().ToString()),
            new Claim("audience", audience),
            new Claim("issuer", issuer)
        };

        if (!string.IsNullOrEmpty(payload.Roles))
        {
            foreach (var role in payload.Roles.Split(','))
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }
        }

        var securityKey = new SymmetricSecurityKey(key);
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: issuer,
            claims: claims,
            signingCredentials: credentials,
            expires: DateTimeOffset.UtcNow.AddHours(1)
        );

        return new JwtSecurityTokenHandler().WriteToken(token);
    }

    public static UserTokenInfo ParseToken(string token)
    {
        var handler = new JwtSecurityTokenHandler();
        var result = new UserTokenInfo();

        if (string.IsNullOrEmpty(token) || !handler.CanReadToken(token))
            return result;

        var jwtToken = handler.ReadJwtToken(token);
        result.UserId = int.Parse(jwtToken.Claims.First(c => c.Type == "user_id").Value);
        
        if (jwtToken.Claims.Any(c => c.Type == ClaimTypes.Role))
        {
            result.Roles = string.Join(",", jwtToken.Claims
                .Where(c => c.Type == ClaimTypes.Role)
                .Select(c => c.Value));
        }

        return result;
    }
}

サービス登録

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
    var securityConfig = builder.Configuration.GetSection("SecuritySettings");
    var key = Encoding.ASCII.GetBytes(securityConfig["Key"]);

    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(key),
        ValidateIssuer = true,
        ValidIssuer = securityConfig["Issuer"],
        ValidateAudience = true,
        ValidAudience = securityConfig["Audience"],
        ValidateLifetime = true,
        ClockSkew = TimeSpan.Zero
    };
});

設定クラス

public class ConfigReader
{
    private static IConfiguration _config;
    
    public ConfigReader(IConfiguration config)
    {
        _config = config;
    }

    public static string GetValue(params string[] keys)
    {
        return _config[string.Join(":", keys)];
    }

    public static List<T> GetList<T>(params string[] keys)
    {
        var list = new List<T>();
        _config.GetSection(string.Join(":", keys)).Bind(list);
        return list;
    }
}

トークンモデル

public class UserTokenInfo
{
    public int UserId { get; set; }
    public string Roles { get; set; }
    public string Scope { get; set; }
}

設定ファイル例

{
  "SecuritySettings": {
    "Key": "secure_random_key_1234567890",
    "Issuer": "MyApp",
    "Audience": "ClientApp"
  }
}

認証コントローラー

[Route("api/auth")]
[ApiController]
public class AuthController : ControllerBase
{
    [HttpGet("token")]
    public string GenerateToken([FromQuery] string username, [FromQuery] string password)
    {
        var token = TokenManager.GenerateToken(new UserTokenInfo
        {
            UserId = 1001,
            Roles = "admin,developer"
        });
        return token;
    }
}

JWT標準クレーム一覧

クレーム 説明 仕様
user_id ユーザー識別子 rfc7519#section-4
issued_at 発行時刻 rfc7519#section-4
expires_at 有効期限 rfc7519#section-4
audience 受信者 rfc7519#section-4
issuer 発行者 rfc7519#section-4

タグ: .NET 6 JWT authentication Security ASP.NET Core

7月18日 20:18 投稿