如何在最小的 API 中添加身份验证?

Wim*_*ink 1 c# authentication jwt asp.net-core-6.0

因此,我刚刚创建了一个“Echo”服务,这样我就可以更深入地了解 Web 应用程序和 Web API。它有很多路由,在每种情况下它都只会返回一个 JSON 结果,其中包含有关请求和响应的信息。
所以我有一个方法“header”,它将向响应添加一个标头。以及一个将发回 cookie 的方法“cookie”。以及其他一些具有实验路线的方法。
现在我想通过使用身份验证内容和 JWT 来重点关注身份验证,这样我就可以更深入地了解这些东西的作用。因为仅仅通过复制/粘贴其他项目中的内容来添加 AddAuthentication/AddJwtBearer 并不是我想要的。我也不想要一些数据库后端,但下一步我可能想通过 Google、Facebook 和 Twitter 进行 OAuth 身份验证。但现在,我有一个变量accounts来保存一些有效的登录帐户,现在我需要使用它来进行登录并使用此身份验证框架。
没有数据库,没有Azure,没有复杂的东西。只是简单、最少的 API 代码。那是因为这个项目的目的是了解该技术并对其进行实验,我不想专注于其他任何事情。仅认证。(就像我也用它来关注路由的工作原理一样。)
所以我想要的是几个步骤:

  1. 了解如何在最小的项目中添加身份验证。
  2. 创建一条需要身份验证的路由。
  3. 给这个项目添加授权。
  4. 制定一条需要授权的路线。
  5. 将身份添加到该项目。
  6. 创建一条使用身份的路由。

那么,要开始这一切,我该如何执行第 1 步?

var builder = WebApplication.CreateBuilder(args);

Dictionary<string, string> accounts = new Dictionary<string, string>() { { "wim", "123456" }, { "test", "abc123" } };

builder.Services.AddAuthentication()
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Unauthorized/";
        options.AccessDeniedPath = "/Account/Forbidden/";
    })
    .AddJwtBearer(options =>
    {
        options.Audience = "Everyone";
        options.Authority = "Wim";
    });

var app = builder.Build();

app
    .UseHsts()
    .UseAuthentication()
    .MapWhen(ContainsPhp, HandlePhp());

app.MapGet("/", (HttpContext context) => Echo(context));

app.MapGet("/cookie/{name}/{*values}", (HttpContext context, string name, string values) =>
{
    foreach (var value in values.Split("/"))
    {
        context.Response.Cookies.Append($"{name}.{value}", value);
    }
    return Echo(context);
});

app.MapGet("/header/{name}/{*values}", (HttpContext context, string name, string values) =>
{
    context.Response.Headers[name] = values.Split("/").ToArray();
    return Echo(context);
});

app.MapGet("{name}.html", (HttpContext context) => Echo(context));
app.MapGet("/{one}/{two}/{three}.{four}", (HttpContext context, string one, string two, string three, string four, [FromQuery] string five, [FromQuery] string six) =>
{
    context.Response.Headers["one"] = one;
    context.Response.Headers["two"] = two;
    context.Response.Headers["three"] = three;
    context.Response.Headers["four"] = four;
    context.Response.Headers["five"] = five;
    context.Response.Headers["six"] = six;
    return Echo(context);
});

app.MapGet("/{*rest}", (HttpContext context) => Echo(context));
app.MapGet("/echo/", (HttpContext context) => Echo(context));
app.MapGet("/echo/{*rest}", (HttpContext context) => Echo(context));
app.MapGet("{path}.html", (HttpContext context) => Echo(context));

app.Run();

// ----------------------------------------------------------------

bool ContainsPhp(HttpContext context) => context.Request.Path.Value?.ToLower().Contains(".php") ?? false;
Action<IApplicationBuilder> HandlePhp() => applicationBuilder =>
    applicationBuilder.Run((context) => Task.Run(() => context.Response.Redirect("https://www.php.net/")));

IResult Echo(HttpContext httpContext)
{
    return Results.Json(new
    {
        Request = new
        {
            host = httpContext.Request.Host,
            method = httpContext.Request.Method,
            path = httpContext.Request.Path,
            pathBase = httpContext.Request.PathBase,
            route = httpContext.Request.RouteValues,
            scheme = httpContext.Request.Scheme,
            Query = new
            {
                query = httpContext.Request.Query,
                queryString = httpContext.Request.QueryString,
            },
        },
        response = new
        {
            statusCode = httpContext.Response.StatusCode,
            cookies = httpContext.Response.Cookies,
            contentType = httpContext.Response.ContentType,
        },
        headers = new
        {
            response = httpContext.Response.Headers,
            request = httpContext.Request.Headers,
        },
        Connection = new
        {
            protocol = httpContext.Request.Protocol,
            localIpAddress = httpContext.Connection.LocalIpAddress?.ToString(),
            localPort = httpContext.Connection.LocalPort,
            remoteIpAddress = httpContext.Connection.RemoteIpAddress?.ToString(),
            remotePort = httpContext.Connection.RemotePort,
        },
        user = httpContext.User,
        items = httpContext.Items,
    }, new(JsonSerializerDefaults.Web) { WriteIndented = true });
}
Run Code Online (Sandbox Code Playgroud)

我希望响应 JSON 会在用户/身份路径中显示某些内容。正确的?


我获得了工作的身份验证和授权,结果就在这个问题中。但我预计可能有更好的选择,将这一切保持在最低限度。

Gur*_*ron 7

最小 API 支持通过AuthotizeAttribute放置在处理程序上的授权:

app.MapGet("/auth", [Authorize] () => "This endpoint requires authorization.");
Run Code Online (Sandbox Code Playgroud)

或者通过RequireAuthorization端点构建器上的方法调用:

app.MapGet("/auth", () => "This endpoint requires authorization")
   .RequireAuthorization();
Run Code Online (Sandbox Code Playgroud)

阅读更多

要处理身份验证,您可以创建自定义登录端点并将其标记为匿名:

app.MapGet("/login", [AllowAnonymous] () => // ... login user ); // or call AllowAnonymous()
Run Code Online (Sandbox Code Playgroud)