我们正在尝试在使用OWIN的项目中设置白色标签(包括FB,google,Live登录)。有没有办法动态设置他们的API凭据,比如他们是否更改了设置将更改的域。
我认为owin比MVC更早加载?有没有办法可以在Global.asax(Request)上加载它?
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
更新:
换句话说,单个应用程序将托管许多域和子域(白色标签)。
答案 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));
}
);
}