简体   繁体   English

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

[英]MVC website and web-api authentication/authorization failure action (Framework 4.6.1)

I have an MVC web application using identity model authentication and authorization that works very well. 我有一个使用身份模型身份验证和授权的MVC Web应用程序,效果很好。 The same web application also has supports a REST web api. 相同的Web应用程序还支持REST Web API。 Until now, the api calls have all worked unauthorized (by design). 到目前为止,api调用在未经授权的情况下(根据设计)一直有效。 I now have a need to support authenticated api calls, but can't seem to separate the web api authentication failure behavior from that of the web site. 我现在需要支持经过身份验证的api调用,但是似乎无法将Web api身份验证失败行为与网站的行为分开。 It is not appropriate for the web api to redirect to the login page on authentication failure, it should instead return a 401 http response to the caller. Web api在身份验证失败时重定向到登录页面是不合适的,它应该向调用者返回401 http响应。

Controllers for the web pages use the AuthorizeAttribute on routes that should be authorized, and if a user who is not authorized attempts to access them, they are redirected to the login page. 网页的控制器在应被授权的路由上使用AuthorizeAttribute,如果未经授权的用户尝试访问它们,则会将其重定向到登录页面。

I've implemented a custom IAuthenticationFilter so I can tag api calls with a HmacAuthenticationAttribute. 我已经实现了自定义IAuthenticationFilter,因此可以使用HmacAuthenticationAttribute标记api调用。 (I'm pretty sure the specific authentication mechanism here doesn't matter.) When used in a stand-alone web-api prototype, it works perfectly and the response to the client is the correct Http 401 code if authentication fails. (我敢肯定,这里的特定身份验证机制无关紧要。)在独立的Web-api原型中使用时,它可以完美工作,并且如果身份验证失败,则对客户端的响应就是正确的Http 401代码。

When the exact same filter is added to the web api in my real web app, an authentication failure results in a redirect to the login page, and the client receives a Http 200 OK response, with the login page html in the body of the response. 当在我的真实Web应用程序中将完全相同的过滤器添加到Web api时,身份验证失败将导致重定向到登录页面,并且客户端会收到Http 200 OK响应,并且响应页面的正文为html 。 This is obviously NOT the desired behavior. 这显然不是期望的行为。

Can anyone provide any insight on how to separate the web page versus web api responses when using authentication attribute filters? 在使用身份验证属性过滤器时,谁能提供关于如何将网页与Web api响应分开的见解?

If not, I'll need to refactor to not use an attribute, and call my authentication as a method instead, but it doesn't seem that this should be necessary. 如果没有,我将需要重构为不使用属性,而是将身份验证作为方法来调用,但是似乎没有必要这样做。

Your problem is that the authentication middleware runs on the request pipeline for all routes. 您的问题是身份验证中间件在所有路由的请求管道上运行。 You need to configure different authentication methods for different requests. 您需要为不同的请求配置不同的身份验证方法。

Take a look here for more info on Conditional Middleware. 在这里查看有关条件中间件的更多信息。

Let's assume that your api authenticates by an api-key header, and your regular app authenticates via a cookie or bearer token. 假设您的api是通过api-key标头进行身份验证的,而常规应用是通过Cookie或承载令牌进行身份验证的。

In your Startup.cs 在您的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();
});

If you modify your HmacAttribute and put the same code into some middleware then you have 2 different paths for Authentication, and can use Authorize attributes or Policies as required. 如果修改HmacAttribute并把同样的代码放到一些中间件,那么你必须进行身份验证2条不同的路径,并可以使用Authorize属性或政策的要求。

Your middleware might look something like this: 您的中间件可能看起来像这样:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM