首页 文章

ASP.Net Core - 没有API auth错误的重定向

提问于
浏览
17

在我的ASP.NET核心项目中,我得到了一些带有jwt-authorization的API控制器,如下所示:

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

当访问操作FetchAll()的授权失败时,我想要HttpStatusCode.Forbidden作为响应 . 相反,Mvc重新路由到帐户/登录?ReturnUrl = [...]

我试图捕获Redirect-Events并返回Forbidden / Unauthorized覆盖Cookie事件无效:

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);
      }
    },
  });

从不调用这两个事件,并且Visual Studio输出显示将返回fetchall Fails和Account / Login:

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

我希望我的API返回401/403而不是重定向到Login - 如果以上代码不起作用,我该如何实现?

3 回答

  • 8

    更新ASP.NET Core 2.x.

    ASP.NET Core 2.0中的授权稍有变化 . 下面的答案仅对ASP.NET Core 1.x有效 . 对于ASP.NET Core 2.0,请参阅此answer和此GitHub annoucement .

    ASP.NET Core 1.x

    你似乎忘记的是 app.UseIdentity() 也是registers the cookie middleware .

    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);
    

    并且ASP.NET核心标识将 AutomaticChallange 设置为 true 用于cookie( ApplicationCookie )中间件(see source) . 因此重定向到 /Account/Login?ReturnUrl . 您需要在Identity中禁用此选项 .

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

    如果你想要拥有身份's Auth (login to web page) and JWT, you'需要根据网址注册中间件 . 因此,即 app.UseIdentity() 仅针对非api URL注册,而Jwt中间件仅针对以 /api 开头的URL注册 .

    你可以用 .MapWhendocs)来做到这一点 .

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

    现在 branch.UseIdentity() 仅用于不以 /api 开头的URL,这通常是您需要重定向到 /Account/Login 的MVC视图 .

  • 18

    我只是用Barry Dorrans Asp Net Authorization Workshop

    ConfigureServices 我只是添加 services.AddAuthorization(); .

    并在 Configure 添加此代码:

    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;
            }
        }
    }
    

    在Mvc重新路由到帐户/登录?ReturnUrl = [...]并在API中,您将获得401或403 .

  • 0

    微软的web api堆栈设置为开箱即用 . 解决方案是在客户端 .

    将此标头添加到客户端请求:

    'X-Requested-With': 'XMLHttpRequest'
    

    Web api寻找那个 Headers . 如果存在,如果请求未经身份验证,则返回401 . 当标头不存在时,它将重定向返回到登录页面 .

    看到这个https://github.com/aspnet/Security/issues/1394#issuecomment-326445124

    如果您无法修改客户端,我认为您只需要cookie事件中更复杂的代码 .

相关问题