【问题标题】:MVC website and web-api authentication/authorization failure action (Framework 4.6.1)MVC 网站和 web-api 身份验证/授权失败操作(框架 4.6.1)
【发布时间】:2019-10-24 23:44:31
【问题描述】:

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

网页的控制器在应该被授权的路由上使用 AuthorizeAttribute,如果没有被授权的用户试图访问它们,他们会被重定向到登录页面。

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

当在我的真实网络应用程序中将完全相同的过滤器添加到网络 api 时,身份验证失败会导致重定向到登录页面,并且客户端会收到 Http 200 OK 响应,其中登录页面 html 在正文中的回应。这显然不是我们想要的行为。

谁能提供有关在使用身份验证属性过滤器时如何区分网页和 Web api 响应的任何见解?

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

【问题讨论】:

  • ASP.NET MVC 6 已重命名为 ASP.NET Core MVC (v1)
  • 对不起,它是 MVC5,框架 4.6.1。编辑以反映变化。

标签: c# rest asp.net-web-api


【解决方案1】:

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

查看here 了解有关条件中间件的更多信息。

假设您的 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 属性或Policies

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

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

【讨论】:

  • 谢谢 ste-fu。这看起来很有希望,可能会为我指明正确的方向。但是,这不是 asp.net 核心,因此您的代码示例不正确。如果您知道 .Net 4.6.x 等价物,那就太好了。与此同时,我会朝那个方向看。
  • 您是否尝试过将[AllowAnonymous] 属性与您自己的属性配对?
  • 是的,这是我尝试的第一件事。没有明显效果。
猜你喜欢
  • 1970-01-01
  • 2011-07-03
  • 2016-01-12
  • 2012-09-10
  • 1970-01-01
  • 1970-01-01
  • 2016-05-29
  • 1970-01-01
  • 2015-05-20
相关资源
最近更新 更多