必要なパッケージ
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 |