it-swarm.com.de

ASP.Net Core - Keine Umleitung bei API-Authentifizierungsfehler

In meinem ASP.NET Core-Projekt habe ich einige API-Controller mit jwt-Berechtigung wie folgt erhalten:

[Route("api/v1/[controller]")]
public class MyController : Controller
{
  [HttpGet("[action]")]
  [Authorize(Policy = MyPolicy)]
  public JsonResult FetchAll()
  {
  }
}

Wenn die Autorisierung für den Zugriff auf die Aktion FetchAll () fehlschlägt, möchte ich HttpStatusCode.Forbidden als Antwort. Stattdessen führt Mvc eine Weiterleitung zu Konto/Login aus. ReturnUrl = [...]

Ich habe versucht, die Weiterleitungsereignisse festzuhalten und verbotene/nicht autorisierte Überschreibungen der Cookie-Ereignisse ohne Erfolg zurückzugeben:

  app.UseIdentity();

  var tokenValidationParameters = new TokenValidationParameters
  {
    ValidateIssuerSigningKey = true,
    IssuerSigningKey = TokenController.DummyKey,
    ValidateIssuer = false,
    ValidateAudience = false,
    ValidateLifetime = true,
    ClockSkew = TimeSpan.FromMinutes(0)
  };
  app.UseJwtBearerAuthentication(new JwtBearerOptions
  {
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    TokenValidationParameters = tokenValidationParameters,
  });

  app.UseCookieAuthentication(new CookieAuthenticationOptions()
  {
    AutomaticAuthenticate = false,
    AutomaticChallenge = false,
    AuthenticationScheme = "BsCookie",
    CookieName = "access_token",
    TicketDataFormat = new CustomJwtDataFormat(SecurityAlgorithms.HmacSha256, tokenValidationParameters),
    Events = new CookieAuthenticationEvents
    {
      OnRedirectToLogin = context =>
      {
        if (context.Request.Path.StartsWithSegments("/api") && context.Response.StatusCode == (int)HttpStatusCode.OK)
          context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
        else
          context.Response.Redirect(context.RedirectUri);
        return Task.FromResult(0);
      },

      OnRedirectToAccessDenied = context =>
      {
        if (context.Request.Path.StartsWithSegments("/api") && context.Response.StatusCode == (int)HttpStatusCode.OK)
          context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
        else
          context.Response.Redirect(context.RedirectUri);
        return Task.FromResult(0);
      }
    },
  });

Beide Ereignisse werden nie aufgerufen, und die Visual Studio-Ausgabe zeigt, dass fetchall Fails und Account/Login stattdessen zurückgegeben werden:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:6460/api/v1/Lehrer/GetAll application/json 
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Successfully validated the token.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Bearer.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed for user: (null).
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: AuthenticationScheme: Bearer was forbidden.
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware:Information: AuthenticationScheme: Identity.Application was challenged.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action Sam.Learning2.Controllers.LehrerController.GetAll (Sam.Learning2) in 49.7114ms
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 121.6106ms 302 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:6460/Account/Login?ReturnUrl=%2Fapi%2Fv1%2FLehrer%2FGetAll  

Ich möchte, dass meine APIs 401/403 zurückgeben, anstatt zur Anmeldung umgeleitet zu werden. Wie kann ich dies erreichen, wenn der obige Code nicht funktioniert?

17
Sam

Aktualisieren Sie ASP.NET Core 2.x

Die Autorisierung wurde in ASP.NET Core 2.0 geringfügig geändert. Die Antwort unten ist nur für ASP.NET Core 1.x gültig. Für ASP.NET Core 2.0 siehe diese Antwort und diese GitHub Annoucement .

ASP.NET Core 1.x

Was Sie anscheinend vergessen haben, ist, dass app.UseIdentity() auch die Cookie-Middleware registriert .

var options = app.ApplicationServices.GetRequiredService<IOptions<IdentityOptions>>().Value;
app.UseCookieAuthentication(options.Cookies.ExternalCookie);
app.UseCookieAuthentication(options.Cookies.TwoFactorRememberMeCookie);
app.UseCookieAuthentication(options.Cookies.TwoFactorUserIdCookie);
app.UseCookieAuthentication(options.Cookies.ApplicationCookie);

und die ASP.NET Core Identity setzt AutomaticChallange auf true für Middleware für Cookies (ApplicationCookie) ( siehe source ). Daher die Weiterleitung zu /Account/Login?ReturnUrl. Sie müssen diese Option in Identity deaktivieren. 

services.AddIdentity(options =>
{
    options.Cookies.ApplicationCookie.AutomaticChallenge = false;
});

Wenn Sie really über Identity's Auth (Anmeldung an der Webseite) und JWT verfügen möchten, müssen Sie die Middlewares basierend auf der URL registrieren. Das heißt, app.UseIdentity() ist nur für Nicht-API-URLs registriert, und Jwt-Middleware ist nur für URLs registriert, die mit /api beginnen.

Sie können das mit .MapWhen ( docs ) machen.

app.MapWhen(context => !context.Request.Path.StartsWith("/api"), branch => 
{
    branch.UseIdentity();
});

Jetzt wird branch.UseIdentity() nur für URLs verwendet, die nicht mit /api beginnen. Dies sind normalerweise Ihre MVC-Ansichten, bei denen die Weiterleitung zu /Account/Login gewünscht wird. 

18
Tseng

Ich benutze nur Barry Dorrans Asp Net Authorization Workshop

in ConfigureServices füge ich nur services.AddAuthorization(); hinzu.

und in Configure diesen Code hinzufügen:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "Cookie",
    LoginPath = new PathString("/Account/Login/"),
    AccessDeniedPath = new PathString("/Account/Forbidden/"),
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    Events = new CookieAuthenticationEvents()
    {
        OnRedirectToLogin = (ctx) =>
        {
            if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
            {
                ctx.Response.StatusCode = 401;
            }
            else
                ctx.Response.Redirect(ctx.RedirectUri);

            return Task.CompletedTask;
        },
        OnRedirectToAccessDenied = (ctx) =>
        {
            if (ctx.Request.Path.StartsWithSegments("/api") && ctx.Response.StatusCode == 200)
            {
                ctx.Response.StatusCode = 403;
            }
            else
            {
                ctx.Response.Redirect(ctx.RedirectUri);
            }
            return Task.CompletedTask;
        }
    }
}

In Mvc auf Konto/Login umleiten? ReturnUrl = [...] und in der API 401 oder 403.

8
akaco

Der Web-API-Stack von Microsoft ist so eingerichtet, dass er sofort einsatzbereit ist. Die Lösung befindet sich am Client.

Fügen Sie diesen Header der Clientanforderung hinzu:

'X-Requested-With': 'XMLHttpRequest'

Web-API sucht nach diesem Header. Wenn vorhanden, wird eine 401 zurückgegeben, wenn die Anforderung nicht authentifiziert wurde. Wenn der Header nicht vorhanden ist, wird die Weiterleitung zur Anmeldeseite zurückgegeben.

Sehen Sie dies https://github.com/aspnet/Security/issues/1394#issuecomment-326445124

Ich denke, Sie benötigen nur den komplexeren Code in den Cookie-Ereignissen, wenn Sie den Client nicht ändern können.

0
RichardHowells