OWIN认证管道,以及如何正确使用Katana中间件?

Tom*_*nna 34 c# adfs ws-federation owin katana

我最近开始研究新的ASP.Net Identity框架和Katana中间件,那里有大量的代码和文档,但我看到的是很多相互矛盾的信息,我想这是一个代码更新频率增加的结果.

我期待使用WsFederation认证与内部ADFS分2次服,但方式OWIN认证管道工程有我有点困惑,我希望有人能提供一些确切的信息.

具体来说,我感兴趣的是应该连接中间件的顺序以及在各种场景中需要哪些模块,我想摆脱任何不需要的东西,同时确保过程尽可能安全.

例如,它似乎UseWsFederationAuthentication应该与之结合使用UseCookieAuthentication,但我不确定正确的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
                }
            }
        }
    }
);
Run Code Online (Sandbox Code Playgroud)

非常感谢您提供的任何帮助,以帮助我解决这个困惑.

Lar*_*ann 49

正如@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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

context.Authentication.Challenge(authenticationTypes)呼叫将从每个提供的身份验证类型发出身份验证质询.我们将提供一个我们的WS-Federation身份验证类型.

正确的代码

首先,这里是一个简单使用WS-Federation的站点的"最佳"Owin Startup配置示例,如下所示:

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();
}
Run Code Online (Sandbox Code Playgroud)

请注意使用它"WS-Fed Auth (Primary)" AuthenticationType来唯一标识我们配置的WS-Federation中间件实例.例如"WS-Fed Auth (Secondary)",如果您有这个要求,这意味着您可以使用带有单独的WS-Federation服务器作为后备.

此配置将执行以下操作:

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

错误的代码

所以有几种方法你可以在这里出错.

不提供默认身份验证类型

为了实验,我尝试这样做,你会马上看到问题所在:

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

    app.SetDefaultSignInAsAuthenticationType(x);
}
Run Code Online (Sandbox Code Playgroud)

第一次通话将为您提供您在第一条评论中提到的例外情况:

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

正确 - 因为默认情况下,Microsoft.Owin.Security管道不会假设您将要使用的中间件(即,Microsoft.Owin.Security.Cookies甚至不知道存在),因此它不知道应该是什么默认值.

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

这花了我很多时间今天,因为我真的不知道我在做什么:

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

    // ... remainder of configuration
}
Run Code Online (Sandbox Code Playgroud)

因此,这将继续尝试在每次调用时使用WS-Federation 对调用者进行身份验证.这并不是那么昂贵,而是WS-Federation中间件实际上会对每个请求发出挑战.因此,您无法进入,并且您看到大量登录URL飞过您的身边.:P

可能性

那么在管道中拥有所有这些灵活性的好处是,你可以做一些非常酷的事情.例如,我有一个域内有两个不同的Web应用程序,在不同的子路径下运行,如:example.com/fooexample.com/bar.您可以使用Owin的映射功能(如app.Map(...))来为每个应用程序设置完全不同的身份验证管道.在我的例子中,一个使用WS-Federation,而另一个使用客户端证书.试图在整体System.Web框架中做到这一点将是可怕的.:P