OWIN 身份验证管道正确使用 Katana 中间件? [英] OWIN Authentication Pipeline To Use Katana Middleware Correctly?

查看:27
本文介绍了OWIN 身份验证管道正确使用 Katana 中间件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm looking to use WsFederation Authentication against an internal ADFS 2 service and to use the OWIN authentication pipeline .

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

What is considered to be the order in which middleware should be hooked up and which modules are required in various scenarios with minimal code?

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

For example, it would appear that UseWsFederationAuthentication should be used in conjunction with UseCookieAuthentication, but I'm not sure what the correct AuthenticationType would be (this post suggests that it's just an identifier string, but is it's value significant?) or even if we still need to use SetDefaultSignInAsAuthenticationType.

我还注意到 Katana 项目讨论板上的这个主题,其中 Tratcher 提到了一个常见的错误,但对于代码的哪一部分出错不是很具体.

I also noticed this thread on the Katana Project discussions board, where Tratcher mentions a common mistake, but isn't very specific as to which part of the code is in error.

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

The following (with a custom SAML Token handler to read the token string into a valid XML document), works, but is it optimal?

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

推荐答案

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

As @Tratcher said, the AuthenticationType parameter is used by Microsoft.Owin.Security as a key to do lookups of authentication middleware instances.

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

The code below will use the following simple helper method to require that all requests are authenticated. In practice you're more likely to use an [Authorize] attribute on sensitive controllers, but I wanted an example that doesn't rely on any frameworks:

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 身份验证类型.

The context.Authentication.Challenge(authenticationTypes) call will issue an authentication challenge from each of the provided authentication types. We're just going to provide the one, our WS-Federation authentication type.

首先,这里是一个最佳"Owin Startup 配置示例,该示例仅用于像您一样仅使用 WS-Federation 的站点:

So first, here's an example of the "optimal" Owin Startup configuration for a site that's simply using WS-Federation, as you are:

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-Fed Auth (Secondary)" 和单独的 WS-Federation 服务器作为后备,如果您有该要求.

Note the use of the "WS-Fed Auth (Primary)" AuthenticationType to uniquely identify the WS-Federation middleware instance we've configured. This means that you could, for example, use a "WS-Fed Auth (Secondary)" with a separate WS-Federation server as a fallback, if you had that requirement.

此配置将执行以下操作:

This configuration will do the following:

  1. 首先,告诉 Owin 安全管道,默认情况下我们要使用默认的 CookeAuthentication AthenticationType 值对请求进行身份验证.(这只是 CookieAuthenticationDefaults 类上的一个常量字符串,它是 CookieAuthenticationOptions.AuthenticationType 属性使用的默认值.)
  2. 接下来,使用所有默认选项注册一个 cookie 身份验证中间件实例,因此它对应于我们在步骤 1 中设置为默认值的 AuthenticationType 键.
  3. 接下来,使用我们在 Web.config 文件中定义的选项和自定义 AuthenticationType 值注册一个 WS-Federation 身份验证中间件实例,以便我们以后可以参考.
  4. 在完成所有身份验证中间件注册后,我们告诉管道对所有请求进行身份验证(通过调用 Microsoft.Owin.Security 方法的自定义帮助程序方法向任何未经身份验证的请求发出质询)
  5. 最后,如果用户已通过身份验证,则显示欢迎页面!
  1. First, tell the Owin security pipeline that by default we want to authenticate requests with the default CookeAuthentication AthenticationType value. (That's just a constant string on that CookieAuthenticationDefaults class, and it's the default value used by the CookieAuthenticationOptions.AuthenticationType property.)
  2. Next, register a cookie authentication middleware instance with all default options, so it corresponds to the AuthenticationType key that we set as the default in step 1.
  3. Next, register a WS-Federation authentication middleware instance with options that we define in the Web.config file, and with a custom AuthenticationType value so we can refer to it later.
  4. After all the authentication middleware registrations are done, we tell the pipeline to authenticate all requests (via our custom helper method that calls the Microsoft.Owin.Security methods for issuing challenges to any unauthenticated request)
  5. Finally, if the user has been authenticated, show the welcome page!

错误代码

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

Wrong Code

So there are a couple ways you can go wrong here.

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

To experiment, I tried doing this, and you'll see right away what the problem is:

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

    app.SetDefaultSignInAsAuthenticationType(x);
}

第一次调用会给你你在第一条评论中提到的例外:

That first call will give you the exception you mentioned in your first comment:

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

"A default value for SignInAsAuthenticationType was not found in IAppBuilder Properties. This can happen if your authentication middleware are added in the wrong order, or if one is missing."

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

Right - because by default the Microsoft.Owin.Security pipeline doesn't assume anything about the middleware you're going to use (i.e., Microsoft.Owin.Security.Cookies isn't even known to be present), so it doesn't know what should be the default.

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

This cost me a lot of time today because I didn't really know what I was doing:

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

    // ... remainder of configuration
}

因此,这将继续尝试在每次调用时使用 WS-Federation 验证调用者.这并不是说太昂贵了,而是 WS-Federation 中间件实际上会发出对每个请求都是一个挑战. 所以你永远无法进入,你会看到一大堆登录 URL 从你身边飞过.:P

So, that's going to keep trying to authenticate the caller with WS-Federation on every call. It's not that that's super-expensive, it's that the WS-Federation middleware will actually issue a challenge on every request. So you can't ever get in, and you see a whole lot of login URLs fly past you. :P

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

So what's great about having all this flexibility in the pipeline is that you can do some really cool things. For instance, I have a domain with two different web apps inside of it, running under different subpaths like: example.com/foo and example.com/bar. You can use Owin's mapping functionality (as in app.Map(...)) to set up a totally different authentication pipeline for each of those apps. In my case, one is using WS-Federation, while the other is using client certificates. Trying to do that in the monolithic System.Web framework would be horrible. :P

这篇关于OWIN 身份验证管道正确使用 Katana 中间件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆