在我的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 = [...]
我尝试捕获重定向事件并返回禁止/未授权覆盖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 - 如果以上代码不起作用,我该如何实现?
答案 0 :(得分:18)
ASP.NET Core 2.0中的授权发生了一些变化。下面的答案仅对ASP.NET Core 1.x有效。对于ASP.NET Core 2.0,请参阅此answer和此GitHub annoucement。
您似乎忘记的是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;
});
如果确实想要拥有Identity的Auth(登录网页)和JWT,则需要根据网址注册中间件。因此,app.UseIdentity()
仅针对非api url注册,Jwt中间件仅注册以/api
开头的网址。
您可以使用.MapWhen
(docs)执行此操作。
app.MapWhen(context => !context.Request.Path.StartsWith("/api"), branch =>
{
branch.UseIdentity();
});
现在branch.UseIdentity()
仅适用于不以/api
开头的网址,这些网址通常是您需要重定向到/Account/Login
的MVC视图。
答案 1 :(得分:8)
我只使用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.
答案 2 :(得分:0)
Microsoft的Web API堆栈已设置为立即可用。解决方案是在客户端。
将此标头添加到客户端请求中:
'X-Requested-With': 'XMLHttpRequest'
Web api查找该标头。如果请求未经验证,则存在时返回401。如果标题不存在,它将返回重定向到登录页面。
查看此https://github.com/aspnet/Security/issues/1394#issuecomment-326445124
我认为,如果您无法修改客户端,则只需在Cookie事件中使用更复杂的代码即可。