【问题标题】:ASP.Net Core - no redirect on API auth errorASP.Net Core - API 身份验证错误没有重定向
【发布时间】:2017-05-23 21:58:21
【问题描述】:

在我的 ASP.NET Core 项目中,我得到了一些带有 jwt 授权的 API 控制器,如下所示:

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

当访问操作 FetchAll() 的授权失败时,我希望 HttpStatusCode.Forbidden 作为响应。相反,Mvc 会重新路由到 Account/Login?ReturnUrl=[...]

我试图捕获重定向事件并返回 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 失败和帐户/登录将被返回:

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 而不是重定向到登录 - 如果上述代码不起作用,我该如何实现?

【问题讨论】:

  • 别忘了,app.UseIdentity() 还注册了一个 Cookie Authentication 中间件,其中有AutomaticChallange = true。您是否仍然在使用 ASP.NET Core 1.0?我认为要记住,在较新的版本中,使用 AutomaticChallange 的中间件不应超过一个

标签: authentication asp.net-core jwt


【解决方案1】:

更新 ASP.NET Core 2.x

授权在 ASP.NET Core 2.0 中有所改变。下面的答案仅对 ASP.NET Core 1.x 有效。对于 ASP.NET Core 2.0,请参阅 answerGitHub 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 Core 标识将 AutomaticChallange 设置为 true 用于 cookie (ApplicationCookie) 中间件 (see source)。因此重定向到/Account/Login?ReturnUrl。您需要在 Identity 中禁用此选项。

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

如果您真的想要拥有 Identity 的 Auth(登录网页)和 JWT,则需要根据 url 注册中间件。因此,即 app.UseIdentity() 仅注册非 api url,而 Jwt 中间件仅注册以 /api 开头的 url。

您可以使用.MapWhen (docs) 做到这一点。

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

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

【讨论】:

【解决方案2】:

我只使用 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 中重新路由到 Account/Login?ReturnUrl=[...] 并在 API 中您将获得 401 或 403。

【讨论】:

  • 我不知道为什么这适用于我的 JWT 不记名令牌失败,但确实如此。谢谢。
【解决方案3】:

Microsoft 的 Web api 堆栈设置为开箱即用。解决方案在客户端。

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

'X-Requested-With': 'XMLHttpRequest'

Web api 会查找该标头。如果请求未经身份验证,则返回 401。当标头不存在时,它会将重定向返回到登录页面。

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

如果你不能修改客户端,我认为你只需要在 cookie 事件中更复杂的代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-22
    • 2018-08-19
    • 1970-01-01
    • 2018-07-07
    • 2018-03-15
    • 2016-04-02
    • 1970-01-01
    • 2018-06-15
    相关资源
    最近更新 更多