0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

.NET 8公開後、JWT認証におけるAOTの解決

Posted at

.NET 8がリリースされたことにより、AOTによるJWT認証も完了し、基本的なAOT APIを構築することが可能となりました。これにより、何らかのシンプルなAPIプロジェクトにAOTを導入できるようになりました。AOTの利点については、以下を参照してください:
https://learn.microsoft.com/zh-cn/aspnet/core/fundamentals/native-aot?view=aspnetcore-8.0。
以下は、JWT認証を追加したシンプルなデモです、時間が限られているため、参考までにしてください。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security;
using System.Security.Claims;
using System.Text;
using System.Text.Json.Serialization;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.ConfigureHttpJsonOptions(options =>{
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default);
});

#region 策略
builder.Services
    .AddAuthorization(options =>
    {
        //策略名を追加
        options.AddPolicy("Permission", policyBuilder => policyBuilder.AddRequirements(new PermissionRequirement()));
    }).AddSingleton(new List<Permission> {
    new Permission { RoleName = "admin", Url = "/Policy", Method = "get" },
    new Permission { RoleName = "admin", Url = "/todos", Method = "get" },
}).AddSingleton<IAuthorizationHandler, PermissionHandler>().AddAuthentication(options =>{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(opt =>{
    //トークン検証パラメータ
    opt.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("1234567890abcdefg1234567890abcdefg")),
        ValidateIssuer = true,
        ValidIssuer = "http://localhost:5274",
        ValidateAudience = true,
        ValidAudience = "http://localhost:5274",
        ClockSkew = TimeSpan.Zero,
        RequireExpirationTime = true,
    };
});
#endregion

var app = builder.Build();

var sampleTodos = new Todo[] {
    new(1, "Walk the dog"),
    new(2, "Do the dishes", DateOnly.FromDateTime(DateTime.Now)),
    new(3, "Do the laundry", DateOnly.FromDateTime(DateTime.Now.AddDays(1))),
    new(4, "Clean the bathroom"),
    new(5, "Clean the car", DateOnly.FromDateTime(DateTime.Now.AddDays(2)))
};

var todosApi = app.MapGroup("/todos");
todosApi.MapGet("/", () => sampleTodos).RequireAuthorization("Permission"); ;
todosApi.MapGet("/{id}", (int id) =>
    sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
        ? Results.Ok(todo)
        : Results.NotFound()).RequireAuthorization("Permission");

#region 策略
app.MapGet("/login", () =>{
    //JWTSecurityTokenHandlerでtokenを生成
    return new JwtSecurityTokenHandler().WriteToken(
        new JwtSecurityToken(
            issuer: "http://localhost:5274",
            audience: "http://localhost:5274",
            claims: new Claim[] {
                new Claim(ClaimTypes.Role, "admin"),
                new Claim(ClaimTypes.Name, "桂素伟")
            },
            notBefore: DateTime.UtcNow,
            expires: DateTime.UtcNow.AddSeconds(500000),
            signingCredentials: new SigningCredentials(
                new SymmetricSecurityKey(Encoding.ASCII.GetBytes("1234567890abcdefg1234567890abcdefg")),
                SecurityAlgorithms.HmacSha256Signature)
            )
        );
});
app.MapGet("/policy", (ClaimsPrincipal user) => $"こんにちはユーザーさん:{user.Identity?.Name}, ロール:{user.Claims?.Where(s => s.Type == ClaimTypes.Role).First().Value}. これはポリシーです!").RequireAuthorization("Permission");
#endregion

app.Run();

public record Todo(int Id, string? Title, DateOnly? DueBy = null, bool IsComplete = false);

[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext{}

#region 策略
public class PermissionRequirement : IAuthorizationRequirement{}
public class Permission{
    public string? RoleName { get; set; }
    public string? Url { get; set; }
    public string? Method { get; set; }
}

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>{
    private readonly List<Permission> _userPermissions;
    public PermissionHandler(List<Permission> permissions)
    {        _userPermissions = permissions;    }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
    {
        if (context.Resource is DefaultHttpContext)
        {
            var httpContext = context.Resource as DefaultHttpContext;
            var questPath = httpContext?.Request?.Path;
            var method = httpContext?.Request?.Method;
            var isAuthenticated = context?.User?.Identity?.IsAuthenticated;
            if (isAuthenticated.HasValue && isAuthenticated.Value)
            {
                var role = context?.User?.Claims?.SingleOrDefault(s => s.Type == ClaimTypes.Role)?.Value;
                if (_userPermissions.Where(w => w.RoleName == role && w.Method?.ToUpper() == method?.ToUpper() && w.Url?.ToLower() == questPath).Count() > 0)
                {
                    context?.Succeed(requirement);
                }
                else
                {
                    context?.Fail();
                }
            }
        }
        return Task.CompletedTask;
    }
}
#endregion

デモ結果は以下のとおりです:

ログイン:
alt 画像
ログイン情報を見る:
alt 画像
データインターフェースを確認する:
alt 画像
まだ多くのテンプレートがAOTをサポートしていない、または完全にサポートしていない状況です。以下は現在の.NET 8の対応状況です。
機能 完全サポート 部分サポート 未サポート
gRPC: ✅完全サポート
Minimal APIs: ✅部分サポート
MVC: ❌未サポート
Blazor: ❌未サポート
SignalR :❌未サポート
JWT認証: ✅完全サポート
その他の認証: ❌未サポート
CORS: ✅完全サポート
ヘルスチェック: ✅完全サポート
HTTPログ記録: ✅完全サポート
ローカライゼーション: ✅完全サポート
出力キャッシュ: ✅完全サポート
レート制限: ✅完全サポート
リクエスト解凍: ✅完全サポート
レスポンスキャッシュ: ✅完全サポート
レスポンス圧縮: ✅完全サポート
リライト: ✅完全サポート
セッション:❌未サポート
SPA :❌未サポート
静的ファイル :✅完全サポート
WebSockets: ✅完全サポート

(Translated by GPT)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?