【问题标题】:Setup OWIN dynamically (by domain)动态设置 OWIN(按域)
【发布时间】:2014-08-16 20:33:33
【问题描述】:

我们正在尝试在使用 OWIN(包括 FB、谷歌、实时登录)的项目上设置白标。有没有办法动态设置他们的 API 凭证,比如如果他们改变了域,设置就会改变。

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

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

更新:

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

【问题讨论】:

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


    【解决方案1】:

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

    假设您有 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(...);
            });
        }
    }
    

    【讨论】:

    • 链接已失效
    【解决方案2】:

    我刚刚做了一个练习,试图做到这一点。不幸的是,没有办法在启动后直接将中间件注入到基于 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));
    

    首先,请注意,这会使用 MapWhenMiddleware 类型调用 app.Use。这意味着您面临与以前相同的限制 - 这一切都必须预先完成。分支中间件也将在初始化完成之前被烘焙:见最后一行: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(或其等效项)的作用。

    【讨论】:

      【解决方案3】:

      我正在进行一项 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 管道是没有意义的。正确的解决方案是拥有可扩展的中间件,这就是我的建议。

      【讨论】:

      • 我看到了一些关于这个主题的帖子。只是想知道你是怎么做的,我需要在运行时更改 appID 和相关的秘密。由于 owin 是提前初始化的,我无法修改控制器中的任何内容。
      【解决方案4】:

      我已经设法让它工作了。问题是 MapWhen 的执行顺序与人们认为的不完全一致。

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

      MapWhen的第一个参数是一个返回条件的函数。如果它评估为 true,它将从缓存中返回相应的配置,因为此时它已经生成。如果 this 之前总是返回 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));
              }
          );
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-19
        • 1970-01-01
        • 2021-06-08
        • 1970-01-01
        • 1970-01-01
        • 2016-01-12
        • 2018-12-02
        • 1970-01-01
        相关资源
        最近更新 更多