如何在多租户MVC 5应用程序中处理身份验证

时间:2014-11-27 04:37:32

标签: asp.net-mvc entity-framework authentication asp.net-identity multi-tenant

我正在尝试构建一个使用单个数据库的多租户mvc 5站点,并在Sql Server中按架构区分租户。 我从默认的Mvc 5模板开始,并更新了提供的ApplicationDBContext,以获取一个字符串,指定用于该租户的模式,如此。

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    private string _tenantSchema;
    public ApplicationDbContext(string tenantSchema)
        : base("Dev", throwIfV1Schema: false)
    {
        _tenantSchema = tenantSchema;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema(_tenantSchema);
        base.OnModelCreating(modelBuilder);
    }

    public static ApplicationDbContext Create(string tenantSchema)
    {
        return new ApplicationDbContext(tenantSchema);
    }
}

然后在App_Start \ IdentityConfig.cs中我更新了ApplicationUserClass的Create方法,使用Request.Host值的第一部分作为tenantSchema,如此

 public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var tenantSchema = context.Request.Host.Value.Split('.')[0];
        var ctx = new ApplicationDbContext(tenantSchema);

        var userStore = new UserStore<ApplicationUser>(ctx);
        var manager = new ApplicationUserManager(userStore);

所以,如果我要登录到site1.mysite.dev,它将对site1架构中的表进行身份验证是sql server。

当我启动网站并使用site1子域访问它时,它正确地使用site1架构来验证我。但是,如果我在浏览器地址栏中更改url并再次登录,它仍然会针对site1架构进行验证。

如何配置应用程序以更正架构以检查每个请求的身份验证?

1 个答案:

答案 0 :(得分:1)

虽然您显示的代码应该有点工作(但是由于您遗漏了其他关键部分,例如Startup.Auth,很难说,但我不会这样做)。我会改为将ApplicationDbContext.Create方法更改为:

public static ApplicationDbContext Create(
           IdentityFactoryOptions<ApplicationDbContext> options, IOwinContext context)
{
    var tenantSchema = context.Request.Host.Value.Split('.')[0];
    return new ApplicationDbContext(tenantSchema);
}

然后我会改变我的Startup.Auth:

app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create); <---
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

将其他所有内容保留为默认值。具体做法是:

public static ApplicationUserManager Create(
         IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
{
    var manager = new ApplicationUserManager(
                  new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
   ....

但是,您的真正问题很可能是您的模型构建器在第一次创建时缓存在应用程序域中。这在以下文档中引用:

http://msdn.microsoft.com/en-us/library/system.data.entity.dbcontext.onmodelcreating(v=vs.113).aspx

  

通常,只有在创建派生上下文的第一个实例时才调用此方法一次。然后缓存该上下文的模型,该模型适用于app域中上下文的所有其他实例。可以通过在给定的ModelBuidler上设置ModelCaching属性来禁用此缓存,但请注意,这会严重降低性能。通过直接使用DbModelBuilder和DbContextFactory类,可以更好地控制缓存。

在EF codeplex网站上,关于EF和多租户使用的这个主题(这里有太长时间不详述)实际上有一个很好的讨论:

https://entityframework.codeplex.com/discussions/462765