如何正确使用Katana中间件来构建OWIN身份验证管道?

36

我希望使用 WsFederation 认证来对内部 ADFS 2 服务进行身份验证,并使用 OWIN 身份验证管道。

在各种情况下,哪些中间件应按什么顺序连接,以及需要哪些模块来实现最小的代码?


例如,似乎应该将 UseWsFederationAuthenticationUseCookieAuthentication 结合使用,但我不确定正确的 AuthenticationType 是什么( 文章建议它只是一个标识符字符串,但它的值是否重要?)或者是否仍然需要使用 SetDefaultSignInAsAuthenticationType

我还注意到了 Katana 项目讨论版上 Tratcher 提到的一个常见错误,但他没有非常明确地指出代码的哪个部分出现了问题。

以下内容(使用自定义 SAML 令牌处理程序将令牌字符串读入有效的 XML 文档中)可以工作,但是否最优?

var appURI = ConfigurationManager.AppSettings["app:URI"];
var fedPassiveTokenEndpoint = ConfigurationManager.AppSettings["wsFederation:PassiveTokenEndpoint"];
var fedIssuerURI = ConfigurationManager.AppSettings["wsFederation:IssuerURI"];
var fedCertificateThumbprint = ConfigurationManager.AppSettings["wsFederation:CertificateThumbprint"];

var audienceRestriction = new AudienceRestriction(AudienceUriMode.Always);

audienceRestriction.AllowedAudienceUris.Add(new Uri(appURI));

var issuerRegistry = new ConfigurationBasedIssuerNameRegistry();

issuerRegistry.AddTrustedIssuer(fedCertificateThumbprint, fedIssuerURI);

app.UseCookieAuthentication(
    new CookieAuthenticationOptions
    {
        AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType // "Federation"
    }
);

app.UseWsFederationAuthentication(
    new WsFederationAuthenticationOptions
    {
        Wtrealm = appURI,
        SignOutWreply = appURI,
        Configuration = new WsFederationConfiguration
        {
            TokenEndpoint = fedPassiveTokenEndpoint
        },
        TokenValidationParameters = new TokenValidationParameters
        {
            AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
        },
        SecurityTokenHandlers = new SecurityTokenHandlerCollection
        {                        
            new SamlSecurityTokenHandlerEx
            {
                CertificateValidator = X509CertificateValidator.None,
                Configuration = new SecurityTokenHandlerConfiguration
                {
                    AudienceRestriction = audienceRestriction,
                    IssuerNameRegistry = issuerRegistry
                }
            }
        }
    }
);

另外,如果我用元数据位置替换WsFederationAuthenticationOptions.Configuration,突然开始看到以下错误...“在IAppBuilder属性中未找到SignInAsAuthenticationType的默认值。如果您的身份验证中间件添加顺序不正确或缺少其中之一,则可能会发生这种情况。” - Tom Tregenna
1
请尝试访问以下链接:https://github.com/AzureADSamples/WebApp-WSFederation-DotNet/blob/master/WebApp-WSFederation-DotNet/App_Start/Startup.Auth.cs - Tratcher
2
每个身份验证中间件都需要自己独特的AuthenticationType。一些中间件还有一个SignInAsAuthenticationType字段,用于指定另一个中间件应该使用的AuthenticationType以保留其身份验证结果。SetDefaultSignInAsAuthenticationType设置了中间件默认应该使用的SignInAsAuthenticationType值。 - Tratcher
1
因此,在该示例的情况下,每个中间件最终都会得到以下内容:CookieAuthMiddleware - AuthType: Cookies;WsFedAuthMiddleware - AuthType: WsFed,SignInAsAuthenticationType: Cookies。 - Tratcher
“很多相互矛盾的信息,我想这是代码更新频率增加的结果。” -> “我完全同意!一些博客文章应该从微软网站上进行更正/删除…” - Matthieu
显示剩余2条评论
1个回答

53

如@Tratcher所说,AuthenticationType参数被Microsoft.Owin.Security用作查找身份验证中间件实例的键。

下面的代码将使用以下简单的辅助方法要求所有请求都经过身份验证。在实践中,您更有可能在敏感控制器上使用[Authorize]属性,但我想提供一个不依赖任何框架的示例:

private static void AuthenticateAllRequests(IAppBuilder app, params string[] authenticationTypes)
{
    app.Use((context, continuation) =>
    {
        if (context.Authentication.User != null &&
            context.Authentication.User.Identity != null &&
            context.Authentication.User.Identity.IsAuthenticated)
        {
            return continuation();
        }
        else
        {
            context.Authentication.Challenge(authenticationTypes);
            return Task.Delay(0);
        }
    });
}

context.Authentication.Challenge(authenticationTypes)调用将从提供的每种认证类型中发出认证挑战。 我们只会提供一种,即我们的WS-Federation认证类型。

正确的代码

首先,这是一个示例,展示了适用于仅使用WS-Federation的站点的“最佳”的Owin启动配置:

public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

    app.UseCookieAuthentication(new CookieAuthenticationOptions());

    app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
    {
        AuthenticationType = "WS-Fed Auth (Primary)",
        Wtrealm = ConfigurationManager.AppSettings["app:URI"],
        MetadataAddress = ConfigurationManager.AppSettings["wsFederation:MetadataEndpoint"]
    });

    AuthenticateAllRequests(app, "WS-Fed Auth (Primary)");

    app.UseWelcomePage();
}
请注意使用 "WS-Fed Auth (Primary)" AuthenticationType 来唯一标识我们配置的 WS-Federation 中间件实例。这意味着,例如,如果您有此要求,可以使用单独的 WS-Federation 服务器和 "WS-Fed Auth (Secondary)" 作为备用。
此配置将执行以下操作:
  1. 首先,告诉 Owin 安全管道默认情况下使用默认的 CookeAuthentication AthenticationType 值进行请求身份验证。(它只是 CookieAuthenticationDefaults 类上的一个常量字符串,并且是 CookieAuthenticationOptions.AuthenticationType 属性使用的默认值)
  2. 接下来,注册具有所有默认选项的 cookie 身份验证中间件实例,使其对应于我们在第一步中设置为默认值的 AuthenticationType 键。
  3. 接下来,使用我们在 Web.config 文件中定义的选项注册一个 WS-Federation 身份验证中间件实例,并使用自定义的 AuthenticationType 值引用它。
  4. 完成所有身份验证中间件注册后,我们通知管道验证 所有 请求(通过调用 Microsoft.Owin.Security 方法发出挑战以处理任何未经身份验证的请求的自定义帮助程序方法)
  5. 最后,如果用户已经通过身份验证,则显示欢迎页面!

错误的代码

因此,您可以走错几种方式。

未提供默认身份验证类型

为了尝试,我尝试了这样做,您会立即看到问题是什么:

public void Configuration(IAppBuilder app)
{
    var x = app.GetDefaultSignInAsAuthenticationType();

    app.SetDefaultSignInAsAuthenticationType(x);
}

你在第一条评论中提到的异常会在第一次调用时抛出:

"IAppBuilder属性中未找到SignInAsAuthenticationType的默认值。如果您的身份验证中间件添加顺序错误或者缺少其中一个中间件,可能会发生这种情况。"

是的 - 因为默认情况下,Microsoft.Owin.Security管道并不假定你将要使用什么样的中间件(例如,甚至不知道是否有Microsoft.Owin.Security.Cookies),所以它不知道应该是什么默认值。

使用错误的默认身份验证类型

今天我因为不知道自己在做什么而花费了很多时间:

public void Configuration(IAppBuilder app)
{
    app.SetDefaultSignInAsAuthenticationType("WS-Fed AAD Auth");

    // ... remainder of configuration
}

因此,在每次调用时它都将尝试使用WS-Federation验证呼叫者。这并不是非常昂贵,而是因为WS-Federation中间件实际上会在每个请求上发出挑战。因此,您永远无法进入,并且会看到大量的登录URL飞过。:P

可能性

拥有管道中所有这些灵活性的好处是您可以做一些真正酷的事情。例如,我有一个包含两个不同Web应用程序的域,运行在不同的子路径下,如:example.com/fooexample.com/bar。您可以使用Owin的映射功能(如app.Map(...))为每个应用程序设置完全不同的身份验证管道。在我的情况下,一个正在使用WS-Federation,而另一个正在使用客户端证书。在单片的System.Web框架中尝试这样做将是可怕的。:P


1
非常感谢Lars,我终于抽出时间再次查看这个问题(很抱歉接受时耽搁了!),你和Tratcher在这方面提供了很大的帮助,我总结了我认为我知道的内容,并在这里回答了 - https://dev59.com/jGAg5IYBdhLWcg3wu87o - Tom Tregenna
谢谢Tom。非常感谢。 :) 你的总结很准确。当你说“我的理解是联邦中间件利用Cookie中间件来管理其与身份验证相关的cookie”时,我可能会稍微改一下措辞。联邦中间件甚至不知道任何关于cookie的信息,它只是愚蠢地检查每个请求中的令牌,以便进行身份验证。一旦WS-Fed中间件对请求进行了身份验证,Cookie中间件就知道要发出一个cookie(我不确定是如何做到的,这是管道的某种能力)。所以这只是一个倒置的依赖关系。 - Lars Kemmann
在第三步中,如果没有设置Wreply属性,则无法正常工作。我将其设置为与Wtrealm相同的值,然后它就可以正常工作了。 - Miguel
让我详细说明一下我的先前评论。我发现在AD FS中为您的Relaying Party Trust注册了多个EndPoints时需要Wreply。在这种情况下,如果未设置Wreply,则AD FS将使用默认的EndPoint进行回复,这可能与Wtrealm不同并引起问题。 - Miguel
我已经实现了你的 AuthenticateAllRequests,它执行了挑战,但服务器从未返回控制权,而是停留在一个显示我的登录信息的个人资料页面上。我在这里有一个问题:Authentication Does Not Do A Return Trip on Challenge, Just Shows Current User Profile Page - ΩmegaMan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接