ASP.Net Core - не перенаправлять на ошибку API auth
В моем проекте ASP.NET Core я получил несколько API-контроллеров с поддержкой jwt-авторизации следующим образом:
[Route("api/v1/[controller]")]
public class MyController : Controller
{
[HttpGet("[action]")]
[Authorize(Policy = MyPolicy)]
public JsonResult FetchAll()
{
}
}
Когда авторизация для доступа к действию FetchAll() Fails, я хочу 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 - как я могу достичь этого, если выше код не работает?
Ответы
Ответ 1
Обновление ASP.NET Core 2.x
Авторизация немного изменилась в ASP.NET Core 2.0. Ответ ниже ist справедлив только для ASP.NET Core 1.x. Для ASP.NET Core 2.0 обратитесь к этому answer и этому аннулированию GitHub.
ASP.NET Core 1.x
Что вы, кажется, забыли, так это то, что app.UseIdentity()
также регистрирует промежуточное программное обеспечение cookie.
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);
и Core Identity ASP.NET устанавливает AutomaticChallange
в true
для промежуточного программного обеспечения cookie (ApplicationCookie
) (см. источник). Следовательно, перенаправление на /Account/Login?ReturnUrl
. Вам нужно будет отключить этот параметр в Identity.
services.AddIdentity(options =>
{
options.Cookies.ApplicationCookie.AutomaticChallenge = false;
});
Если вы действительно хотите иметь Identity Auth (login to web page) и JWT, вам нужно будет зарегистрировать middlewares на основе URL-адреса. Так, т.е. app.UseIdentity()
регистрируется только для не-api-адресов, а промежуточное ПО Jwt регистрируется только для URL-адресов, начинающихся с /api
.
Вы можете сделать это с помощью .MapWhen
(docs).
app.MapWhen(context => !context.Request.Path.StartsWith("/api"), branch =>
{
branch.UseIdentity();
});
Теперь branch.UseIdentity()
будет использоваться только для URL-адресов, которые не начинаются с /api
, которые обычно являются вашими представлениями MVC, где требуется перенаправление на /Account/Login
.
Ответ 2
Я просто использую Barry Dorrans Asp Net Авторизация
in 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.