Webapi 委托处理程序返回 401 响应被 Owin Auth 覆盖

Webapi Delegating Handler Returning 401 Response Gets Overridden by Owin Auth

我查看过类似的帖子,其中的解决方案对我不起作用。

我在 IIS 7.x 中托管了一个 MVC 5 站点,该站点为 Web ui - https://www.example.com. Callers can also access api (Webapi 2.2) endpoints to perform certain functions - https://www.example.com/api/x 提供服务。一些 pages/apis 是安全的,而另一些则不是。 mvc/web ui 安全性由配置有 UseCookieAuthenticationUseWsFederationAuthentication.

的 owin 中间件管理

当用户还没有有效的 SAML 令牌时,网络中的安全页面ui 会自动重定向到 ADFS 登录屏幕 - 根据需要。

安全网络 api 需要 ui 在 Auth header 中传递一个单独的 JWT 令牌。

Webapi 托管在与 MVC 相同的应用程序池中。 Webapi 没有控制器,相反,webapiconfig 具有利用 DelegatingHandler 通过 api 到 route/pass 的路由电话。委托处理程序负责检查 JWT 是否包含在 Auth header 中,如果包含则允许它继续到另一个验证 JWT 的内部 Webapi。如果 JWT 不存在,那么 DelegatingHandler returns a 401.

401 return 曾经在短路时工作 ui 继续请求,因此绕过了任何 owin 管道内容。但是,现在当 shortcircuit 触发时,401 不是 returned。相反,请求继续并传递到 Owin 身份验证,然后重定向 (302) 到 ADFS 登录。我不知道为什么。 如果我将响应状态代码更改为 401 以外的内容,Owin Auth 将忽略它。

请看下面的代码:

Global.asax.cs

public class Global : HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);        
    }
}

WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "Apis",
            routeTemplate: "api/{*path}",
            handler: HttpClientFactory.CreatePipeline
            (
                innerHandler: new HttpClientHandler(),
                handlers: new DelegatingHandler[] { new ApiHandler() }
            ),
            defaults: new { path = RouteParameter.Optional },
            constraints: null
        );
    }
}

ApiHandler.cs

internal class ApiHandler : DelegatingHandler
{
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);

        try
        {
            // get jwt from header
            var jwt = GetJWTFromHeader(request.Headers);

            if (jwt == null)
            {
                response.ReasonPhrase = "Token required";
                return await Task.FromResult<HttpResponseMessage>(response);
            }
            else if (!IsValidJWT(jwt))
            {
                response.ReasonPhrase = "Invalid token";
                return await Task.FromResult<HttpResponseMessage>(response);
            }

            response = await base.SendAsync(request, cancellationToken);

        }
        catch (Exception ex)
        {
            // log error
            response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }

        // return result
        return response;
    }
}

Startup.Auth.cs

public partial class Startup
{
    public void ConfigureAuth(IAppBuilder app)
    {
        ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate;

        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(
            new CookieAuthenticationOptions()
            {
                SlidingExpiration = false
            }
        );

        app.UseWsFederationAuthentication(
            new WsFederationAuthenticationOptions
            {
                Wtrealm = ADFS_REALM,
                MetadataAddress = ADFS_METADATA,
                UseTokenLifetime = true,
                TokenValidationParameters = new TokenValidationParameters
                {
                    SaveSigninToken = true
                },

                Notifications = new WsFederationAuthenticationNotifications
                {
                    RedirectToIdentityProvider = async r =>
                    {
                        // do stuff                         
                    },
                    SecurityTokenValidated = async s =>
                    {
                        // if we get here, then UI user has valid saml token
                        // do stuff
                    }
                }
            }
        });

}

感谢您的帮助。如果需要更多详细信息,请告诉我!

看起来你可以使用: https://msdn.microsoft.com/en-us/library/system.web.http.owinhttpconfigurationextensions.suppressdefaulthostauthentication(v=vs.118).aspx config.SuppressDefaultHostAuthentication();

感谢Finallz I was able to refine my search and come across an answer - found here。在我的例子中,我不需要任何特殊的身份验证配置,因为我在 api 处理程序中手动检查了 JWT。但是,通过简单地包含一个映射到我的 api 路径,它自然会覆盖 Owin 安全性:

app.Map("/api", inner =>
{
     // nothing to do here since we don't have any concrete controllers to manage special authorization for
     // we're using apihandlers to pass api traffic through to next stop
});