MVC 网站和 Web-API 身份验证/授权失败操作(框架 4.6.1)



我有一个使用身份模型身份验证和授权的MVC Web应用程序,效果很好。同一 Web 应用程序还支持 REST Web API。到目前为止,API 调用都未经授权(设计)。 我现在需要支持经过身份验证的 api 调用,但似乎无法将 Web api 身份验证失败行为与网站的行为分开。Web api 不适合在身份验证失败时重定向到登录页面,它应该向调用方返回 401 http 响应。

网页的控制器在应授权的路由上使用 AuthorizeAttribute,如果未经授权的用户尝试访问它们,则会将其重定向到登录页面。

我已经实现了一个自定义的 IAuthenticationFilter,因此我可以使用 HmacAuthenticationAttribute 标记 api 调用。(我很确定这里的特定身份验证机制无关紧要。在独立的 web-api 原型中使用时,它可以完美运行,如果身份验证失败,对客户端的响应是正确的 Http 401 代码。

将完全相同的筛选器添加到我的真实 Web 应用中的 Web API 时,身份验证失败会导致重定向到登录页面,并且客户端会收到 Http 200 OK 响应,并在响应正文中显示登录页面 html。这显然不是期望的行为。

任何人都可以提供有关在使用身份验证属性过滤器时如何分离网页与 Web api 响应的任何见解吗?

如果没有,我需要重构以不使用属性,而是调用我的身份验证作为方法,但这似乎不是必需的。

您的问题是身份验证中间件在所有路由的请求管道上运行。您需要为不同的请求配置不同的身份验证方法。

查看此处以获取有关条件中间件的更多信息。

假设您的 api 通过api-key标头进行身份验证,而您的常规应用通过 cookie 或持有者令牌进行身份验证。

在你的Startup.cs

// This will authenticate calling applications based on the "api-key" header
app.UseWhen(context => context.Request.Headers.ContainsKey("api-key"), appBuilder =>
{
appBuilder.UseMiddleware<HmacAuthentication>();
});

// Using the exact opposite, makes all other requests authenticate the normal way
app.UseWhen(context => !context.Request.Headers.ContainsKey("api-key"), appBuilder =>
{
appBuilder.UseAuthentication();
appBuilder.UseIdentityServer();
});

如果您修改 HmacAttribute 并将相同的代码放入某个中间件中,那么您有 2 个不同的身份验证路径,并且可以根据需要使用Authorize属性或策略。

您的中间件可能如下所示:

public class HmacAuthentication
{
private const string ApiKey = "api-key";
private RequestDelegate _next;
public ApiKeyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
bool success = false;
string[] claims = null;
success = GetHeader(context.Request.Headers, out StringValues headerValue);
if (success)
{                
success = ValidateKeyAndGetClaims(headerValue, out claims);                
}
if (success)
{
context.User = GetPrincipal(headerValue, claims);
await _next(context);
}
else
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized");
}            
}
internal bool GetHeader(IHeaderDictionary headers, out StringValues headerValue)
{
return headers.TryGetValue(ApiKey, out headerValue);
}
internal bool ValidateKeyAndGetClaims(string, headerValue, out string[] claims)
{
// Validate the api-key.
// Claims could depend on the key value or could be hardcoded
claims = new [] { "IsAuthenticated" };
return true;
}        
internal ClaimsPrincipal GetPrincipal(string apiKey, string[] claims)
{
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.Name, apiKey));
if (claims != null)
{
foreach(var claim in claims)
{
identity.AddClaim(new Claim(claim, string.Empty));
}
}
return new ClaimsPrincipal(identity);
}
}

最新更新