ASP.NET Identity Multitenant登录问题

时间:2017-08-03 19:48:41

标签: asp.net-identity multi-tenant

我使用标准的ASP.Net Identity cookie身份验证为我的ASP.NET MVC项目设置了多租户。因此,我可以在mysite.com上访问主租户(常规租户以及访问某些租户管理工具),并在t1.mysite.com,t2.mysite.com上访问不同的租户。

在t1.mysite.com上进行的登录在t2.mysite.com上无效,这正是我想要的。但是,如果我登录mysite.com,那么在登录时,访问tX.mysite.com,看起来好像我还在登录。即使我拥有的应用程序的身份也不存在。

有没有办法确保收到的mysite.com身份验证cookie在tX.mysite.com上无效,这样管理员就不会意外地进入非工作租户(GUI可见,但你可以因为登录mysite.com在tx.mysite.com上无效所以不做任何事情,所以最好只显示登录界面。

2 个答案:

答案 0 :(得分:0)

App_Start\Startup.Auth.cs文件中,您应该了解OnValidateIdentity

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    Provider = new CookieAuthenticationProvider
    {
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<Infrastructure.Identity.UserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromMinutes(30),
            regenerateIdentity: (manager, user) => manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie))
    },
    // Other stuff
});           

请参阅参数validateInterval - 默认设置为30分钟。这就是实际将cookie与数据库中的值进行比较的频率。

将值更改为TimeSpan.FromSeconds(30)。这将每隔30秒将cookie与数据库中的值进行比较,如果租户的身份无效,将向用户显示登录门户。

您也可以将其更改为0,但我觉得这会使您的数据库超载 - 在每次请求时都会有几次调用数据库。

CookieDomain上还有一个属性CookieauthenticationOptions属性:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    // other stuff
    CookieDomain = "example.com"
});

这可能对您有所帮助,但由于此项在管道中如此配置,因此在运行时可能很难重新配置。

另一种选择是拥有多个cookie认证中间件,但这只有在您的租户数量众所周知且非常有限的情况下才有效:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    // other stuff
    AuthenticationType = "myTenant1",
    CookieDomain = "t1.example.com"
});

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    // other stuff
    AuthenticationType = "myTenant2",
    CookieDomain = "t2.example.com"
}); 

但这可能会给你带来比实际值得更多的麻烦。

答案 1 :(得分:0)

所以我找到了解决问题的另一种方法。我似乎可以直接挂钩到cookie身份验证中,只要拒绝来自错误租户的身份。

所以我编写了自定义CookieAuthenticationProvider

public class TenantCookieAuthenticationProvider: CookieAuthenticationProvider
{

    public override Task ValidateIdentity(CookieValidateIdentityContext context)
    {
        string tenantId = SubdomainRoute.GetSubdomain(context.Request.Host.Value);
        if (!string.IsNullOrEmpty(tenantId)) // we're trying to access mysite.com from x.mysite.com
        {
            context.RejectIdentity();
            return Task.FromResult<int>(0);
        }
        return base.ValidateIdentity(context);
    }
}

并将其挂钩到Startup.cs

中的身份验证管道中
app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new TenantCookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });

当然,这只是战斗的一半......另一半不是拒绝身份,以防它是一个有效的租户用户。由于我在创建用户时添加了tenantId作为声明,因此我可以提取并比较这些声明,以免拒绝有效的租户用户。所以我改编了ValidateIdentity如下

public override Task ValidateIdentity(CookieValidateIdentityContext context)
    {
        System.Security.Claims.Claim tenantClaim = context.Identity.Claims.FirstOrDefault(x => x.Subject.Name == "tenant");
        string tenantId = SubdomainRoute.GetSubdomain(context.Request.Host.Value);
        if (tenantClaim != null)
        {
            if (tenantId != tenantClaim.Value)
            {
                context.RejectIdentity();
                return Task.FromResult<int>(0);
            }
        }
        else
        {
            if (!string.IsNullOrEmpty(tenantId)) // we're trying to access audm.local from x.audm.local
            {
                context.RejectIdentity();
                  return Task.FromResult<int>(0);
            }
        }
        return base.ValidateIdentity(context);
    }

现在我甚至还有一层额外的安全保障,因此用户无法在租户和#34;之间跳跃。