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

时间:2014-09-04 10:59:45

标签: c# owin adfs katana ws-federation

我最近开始关注新的ASP.Net Identity框架和Katana中间件,那里有大量的代码和文档,但我看到的似乎是很多相互矛盾的信息,我猜这是因为代码更新频率越来越高。

我希望对内部ADFS 2服务使用WsFederation身份验证,但是OWIN身份验证管道的工作方式让我有些困惑,我希望有人可以提供一些权威性信息。

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

例如,似乎UseWsFederationAuthentication应该与UseCookieAuthentication一起使用,但我不确定正确的AuthenticationType是什么({{3} } post表明它只是一个标识符字符串,但它的值是否显着?)或者即使我们仍然需要使用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
                }
            }
        }
    }
);

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

1 个答案:

答案 0 :(得分: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);
        }
    });
}

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

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

此配置将执行以下操作:

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

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

    未提供默认身份验证类型

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

    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 来验证呼叫者。这不是那种超级昂贵的,它和&# #97; WS-Federation中间件实际上会对每个请求发出挑战。所以你无法进入,并且你会看到很多登录URL都飞过你身边。 :P

    的可能性

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