OWIN 身份验证管道正确使用 Katana 中间件?

Posted

技术标签:

【中文标题】OWIN 身份验证管道正确使用 Katana 中间件?【英文标题】:OWIN Authentication Pipeline To Use Katana Middleware Correctly? 【发布时间】:2014-09-04 10:59:44 【问题描述】:

我希望针对内部 ADFS 2 服务使用 WsFederation 身份验证并使用 OWIN 身份验证管道。

什么是中间件的连接顺序,以及在各种场景中需要哪些模块,代码最少?


例如,UseWsFederationAuthentication 似乎应该与UseCookieAuthentication 一起使用,但我不确定正确的AuthenticationType 是什么(this 帖子表明它只是一个标识符字符串,但它的价值是否重要?)或者即使我们仍然需要使用SetDefaultSignInAsAuthenticationType

我还注意到 Katana 项目讨论板上的 this 线程,其中 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 的默认值。如果您的身份验证中间件的添加顺序错误,或者缺少一个。” 试试这个:github.com/AzureADSamples/WebApp-WSFederation-DotNet/blob/… 感谢 Tracher,感谢 Vittorio Bertocci 的身份博客,这是我的原始资料之一。但它似乎仍然没有明确解释为什么例如 UseCookieAuthentication 的 AuthenticationType 选项设置为 cookie 而不是 wsfed,我已经看到在各种示例中都使用了这两种方法。 每个身份验证中间件都需要自己唯一的身份验证类型。一些中间件还有一个 SignInAsAuthenticationType 字段,用于指定另一个中间件的 AuthenticationType,它们应该使用它们来保存其身份验证的结果。 SetDefaultSignInAsAuthenticationType 设置中间件默认用于 SignInAsAuthenticationType 的值。 因此,在该示例中,每个中间件的最终结果如下:CookieAuthMiddleware - AuthType: Cookies; WsFedAuthMiddleware - AuthType:WsFed,SignInAsAuthenticationType:Cookies。 【参考方案1】:

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

下面的代码将使用以下简单的帮助方法来要求所有请求都经过身份验证。实际上,您更有可能在敏感控制器上使用[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 身份验证类型。

正确代码

首先,这里有一个“最佳”Owin Startup 配置示例,它适用于仅使用 WS-Federation 的站点,就像您一样:

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)" 作为后备。

此配置将执行以下操作:

    首先,告诉 Owin 安全管道,默认情况下,我们希望使用 默认的 CookeAuthentication AthenticationType 值对请求进行身份验证。 (这只是 CookieAuthenticationDefaults 类上的一个常量字符串,它是 CookieAuthenticationOptions.AuthenticationType 属性使用的默认值。) 接下来,使用所有默认选项注册一个 cookie 身份验证中间件实例,因此它对应于我们在步骤 1 中设置为默认值的 AuthenticationType 键。 接下来,使用我们在 Web.config 文件中定义的选项注册一个 WS-Federation 身份验证中间件实例,并使用自定义 AuthenticationType 值,以便我们稍后参考。 在完成所有身份验证中间件注册后,我们告诉管道对所有请求进行身份验证(通过我们的自定义帮助方法,该方法调用Microsoft.Owin.Security 方法以向任何未经身份验证的请求发出挑战)李> 最后,如果用户已通过身份验证,则显示欢迎页面!

错误代码

所以这里有几种方法可能会出错。

未提供默认身份验证类型

为了进行实验,我尝试这样做,您会立即看到问题所在:

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

【讨论】:

非常感谢 Lars,我终于有时间再看一遍(抱歉延迟接受!),你自己和 Tratcher 都帮了大忙,我总结了一下在这里回答我认为我知道的内容 - ***.com/questions/23262471/… 谢谢汤姆。非常感激。 :) 你的总结很到位。当您说“我的理解是联邦中间件利用 Cookie 中间件来管理其与身份验证相关的 cookie”时 - 我的措辞可能略有不同。联邦中间件甚至不知道任何关于 cookie 的信息,它只是愚蠢的,并在每个被要求进行身份验证的请求中检查令牌。一旦请求通过 WS-Fed 中间件的身份验证,Cookie 中间件就知道发出 cookie(我不确定如何,这是管道的某些功能)。所以它只是一个反向依赖。 在第 3 步中,如果没有设置 Wreply 属性,它将无法工作。我将它设置为与 Wtrealm 相同的值,然后它就可以工作了。 --- 让我扩展我之前的评论。我发现当您在 AD FS 中为您的中继方信任注册了多个端点时,需要 Wreply。在这种情况下,如果未设置 Wreply,AD FS 将使用可能与 Wtrealm 不同的默认 EndPoint 进行回复并导致问题。 我实现了您的AuthenticateAllRequests,它完成了挑战,并且服务器永远不会返回控制权,它位于显示我的登录信息的个人资料页面上。我在这里有一个问题Authentication Does Not Do A Return Trip on Challenge, Just Shows Current User Profile Page

以上是关于OWIN 身份验证管道正确使用 Katana 中间件?的主要内容,如果未能解决你的问题,请参考以下文章

具有 OpenId owin katana 中间件的多个 MVC 应用程序的单个注销 Identity Server3

OWIN / Katana 的未处理异常全局处理程序?

使用 Microsoft.Owin.Testing.TestServer 进行内存集成测试的身份验证

OWIN与Katana

Owin cookie身份验证设置cookie未保存在浏览器中

如何在 ASP.NET MVC 5.2.3 和 Katana 中正确设置双重身份验证?