动态设置OWIN(按域)

时间:2014-06-26 05:20:21

标签: asp.net-mvc oauth asp.net-mvc-5 owin

我们正在尝试在使用OWIN的项目中设置白色标签(包括FB,google,Live登录)。有没有办法动态设置他们的API凭据,比如他们是否更改了设置将更改的域。

我认为owin比MVC更早加载?有没有办法可以在Global.asax(Request)上加载它?

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }
}

更新:

换句话说,单个应用程序将托管许多域和子域(白色标签)。

4 个答案:

答案 0 :(得分:6)

今天我一直在谷歌搜索相同的东西。然后我在http://aspnet.codeplex.com/SourceControl/latest#Samples/Katana/BranchingPipelines/BranchingPipelines.sln找到了一个很好的OWIN样本,它解释了OWIN的分支功能。如果我正确理解了这个示例,您应该能够根据请求参数配置不同的OWIN堆栈,例如主机头,cookie,路径或使用app.Map()或app.MapWhen()方法的任何内容。

假设您有2个不同的DNS域代表2个具有不同登录配置的客户,您可以根据主机头的值初始化OWIN以使用不同的配置:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {

        app.MapWhen(ctx => ctx.Request.Headers.Get("Host").Equals("customer1.cloudservice.net"), app2 =>
        {
            app2.UseWsFederationAuthentication(...);
        });
        app.MapWhen(ctx => ctx.Request.Headers.Get("Host").Equals("customer2.cloudservice.net"), app2 =>
        {
            app2.UseGoogleAuthentication(...);
        });
    }
}

答案 1 :(得分:3)

我刚刚进行了一次练习,试图做到这一点。不幸的是,启动后无法直接将中间件注入基于Katana的主机。这样做的原因是,为了实际使用中间件,必须将它组合在一起application delegate。 Katana的实现通过调用IAppBuilder.Build(typeof(AppFunc))来实现这一点,其中AppFunc是应用程序委托的指定类型的使用别名:Func<IDictionary<string,object>, Task>,当初始化完成时。这是.Initialize底部的关键行:

AppFunc = (AppFunc)builder.Build(typeof(AppFunc));

您必须配置中间件的唯一机会就是在此之前,在您编写的启动类的配置步骤期间或web.config

为了清楚赢得工作,我正在尝试这样的事情:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HomeController.Initialized += () => ConfigureGoogle(app);
    }

    private void ConfigureGoogle(IAppBuilder app)
    {
        app.UseGoogleAuthentication(/* stuff */);
    }
}

public class HomeController : Controller
{
    public event EventHandler Initialized;

    [Route("/setup/submit"), AcceptVerbs(HttpVerbs.Post)]
    public ActionResult SetupSubmit()
    {
        /* ... */

        Initialized();
    }
}

这不会抛出异常,并且没有明显的错误迹象 - 但它不起作用,因为应用程序委托已由此点组成。 Katana没有为您提供任何重新构建应用程序代表的API(而且我不确定这是一个好主意 - 这样的机制可能会产生无数的错误;例如,在初始化后重新组合应用程序委托时,服务器应该如何处理正在进行的请求?)。

你的另类选择是什么? @DavidFahlander的方法将是正确的方法,但如果你想要实现活力,你仍需要小心。看看.MapWhen做了什么:

// put middleware in pipeline before creating branch
var options = new MapWhenOptions { Predicate = predicate };
IAppBuilder result = app.Use<MapWhenMiddleware>(options);

// create branch and assign to options
IAppBuilder branch = app.New();
configuration(branch);
options.Branch = (AppFunc)branch.Build(typeof(AppFunc));

首先,请注意,这会使用app.Use类型调用MapWhenMiddleware。这意味着您将面临与以前相同的限制 - 这一切都必须事先完成。分支中间件也将在初始化完成之前进行烘焙:请参阅最后一行:branch.Build

你在这里充满活力的唯一希望就是以实现目标的方式使用谓词。这并没有让你100%的方式,但它变得非常接近:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {

        app.MapWhen(ctx => ClientHasWsFederationConfigured() && ctx.Request.Headers.Get("Host").Equals("customer1.cloudservice.net"), app2 =>
        {
            app2.UseWsFederationAuthentication(...);
        });
        app.MapWhen(ctx => ClientHasGoogleAuthConfigured() && ctx.Request.Headers.Get("Host").Equals("customer2.cloudservice.net"), app2 =>
        {
            app2.UseGoogleAuthentication(...);
        });
    }
}

这里的限制将是:

  • 您必须预先配置所有支持的身份验证类型。在应用运行时,您无法添加新的。
  • 每次请求都会运行
  • ClientHasXXXConfigured。根据您的表现,这可能会或可能不会在表现基础上接受。

鉴于您在问题中提供的信息,我认为只要您对ClientHasXXXConfigured(或它的等效物)的作用进行了谨慎,这些权衡就可以了。

答案 2 :(得分:1)

我正在进行Microsoft.Owin.Security设计更改,允许AuthenticationOptions(例如GoogleAuthenticationOptions)中的多租户支持开发人员注入其owin实现的能力。

以下是我对Katana项目团队的建议: https://katanaproject.codeplex.com/discussions/561673

我还有一个工作实现,它位于现有的Microsoft.Owin.Security基础架构之上,并没有从我的设计更改建议中受益。它需要滚动您自己的Auth中间件版本(复制粘贴现有),但它是一个可行的解决方法,直到我可以在Microsoft实现我的设计更改。

https://github.com/kingdango/Owin.OAuth.Multitenant (这很粗糙,我今天早上起来了)

最终,我认为为每个租户开发多个Owin管道只是为了支持多租户是有意义的。正确的解决方案是拥有可扩展的中间件,这就是我提出的建议。

答案 3 :(得分:0)

我设法让这个工作。问题是MapWhen的执行顺序与人们认为它的确无关。

要记住的关键是配置(即MapWhen的第二个参数)在app启动时执行和缓存。因此,仔细考虑您需要多少配置并为每个独特配置运行单独的“app.MapWhen”非常重要。如果您使用多个域并且每个域使用相同的配置,则不需要执行此操作,但是如果每个域的每个配置都是唯一的,则需要为每个域运行MapWhen。在我的情况下,我发现将这些放在foreach块中更容易,因为我需要为每个域提供唯一的配置,因为OpenIDConnect需要为每个域提供唯一的AppID。

MapWhen的第一个参数是一个返回条件的函数。如果它的计算结果为true,它将从缓存中返回相应的配置,因为此时已经生成了相应的配置。如果以前始终返回false并且突然返回true,则不会生成新配置。需要注意的是,由于每个请求都会执行此条件函数,因此必须保持尽可能快速和轻量级。

var domains = new string[] { "abc.com", "def.com" };
var host = HttpContext.Current.Request.ServerVariables["HTTP_HOST"]?.ToLower();
foreach (string domain in domains)
{
    if (!ShouldEnableForDomain(domain) continue;
    app.MapWhen(
        context => host == domain, //if true a config will be used from the cache
        config =>
        {
            //This executes once on app startup (per domain) and will be cached - it is not executed in the context of a request
            Trace.WriteLine(String.Format("Setting up configuration: {0}", domain));
            config.UseOpenIdConnectAuthentication(GetOpenIdOptions(domain));
        }
    );
}