c# - Multi-Tenancy ASP.NET MVC 应用程序的跨域 OWIN 身份验证

标签 c# asp.net asp.net-mvc owin multi-tenant

我正在为 Multi-Tenancy ASP.NET MVC 应用程序使用 OWIN 身份验证。

应用程序和身份验证位于单个应用程序中的一台服务器上,但可以通过许多域和子域访问。例如:

www.domain.com
site1.domain.com
site2.domain.com
site3.domain.com
www.differentdomain.com
site4.differentdomain.com
site5.differentdomain.com
site6.differentdomain.com

我希望允许用户登录这些域中的任何一个并让他们的身份验证 cookie 工作,而不管使用哪个域来访问应用程序。

这就是我的身份验证设置:
public void ConfigureAuthentication(IAppBuilder Application)
{
    Application.CreatePerOwinContext<RepositoryManager>((x, y) => new RepositoryManager(new SiteDatabase(), x, y));

    Application.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        CookieName = "sso.domain.com",
        CookieDomain = ".domain.com",
        LoginPath = new PathString("/login"),
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,  
        Provider = new CookieAuthenticationProvider
        {
            OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<UserManager, User, int>(
                validateInterval: TimeSpan.FromMinutes(30),
                regenerateIdentityCallback: (manager, user) => user.GenerateClaimsAsync(manager),
                getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))
        }
    });

    Application.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}

我还在我的应用程序的根 web.config 中为我的应用程序显式设置了一个机器 key :
<configuration>
    <system.web>
        <machineKey decryption="AES" decryptionKey="<Redacted>" validation="<Redacted>" validationKey="<Redacted>" />
    </system.web>
</configuration>

更新

当我在 domain.com 和 site1.domain.com 之间导航时,此设置按预期工作,但现在它不允许我登录到 differentdomain.com。

我了解 cookie 与单个域相关联。但是,跨多个域持久登录的最简单方法是什么?有没有办法让我从不同的域读取 cookie,解密它,然后为 differentdomain.com 重新创建一个新的 cookie?

最佳答案

由于您需要一些简单的东西,请考虑一下。在您的特定设置中,您实际上只有一个可通过多个域名访问的应用程序,您可以进行简单的“单点登录”。首先,您必须选择负责初始身份验证的单个域名。假设是 auth.domain.com (请记住,它只是域名 - 您的所有域仍然指向单个应用程序)。然后:

  • 假设用户在 domain1.com你发现他没有登录(没有cookie)。你引导他到auth.domain.com登录页面。
  • 假设您已经在那里登录。您会看到请求来自 domain1.com (通过 Referrer header ,或者您可以显式传递域)。您验证这是您的受信任域(重要),并像这样生成身份验证 token :
    var token = FormsAuthentication.Encrypt(
        new FormsAuthenticationTicket(1, "username", DateTime.Now, DateTime.Now.AddHours(8), true, "some relevant data"));
    

    如果您不使用表单例份验证 - 只需使用机器 key 保护一些数据:
    var myTicket = new MyTicket()
    {
        Username = "username",
        Issued = DateTime.Now,
        Expires = DateTime.Now.AddHours(8),
        TicketExpires = DateTime.Now.AddMinutes(1)
    };
    using (var ms = new MemoryStream()) {
        new BinaryFormatter().Serialize(ms, myTicket);
        var token = Convert.ToBase64String(MachineKey.Protect(ms.ToArray(), "auth"));
    }
    

    所以基本上你以与 asp.net 相同的方式生成你的 token 。由于您的网站都在同一个应用程序中 - 无需担心不同的机器 key 。
  • 您将用户重定向回 domain1.com ,在查询字符串中传递加密 token 。见 here例如关于这方面的安全影响。当然,我想您使用 https,否则无论如何设置(无论是否“单点登录”)都是安全的。这在某些方面类似于 asp.net“无cookie”身份验证。
  • domain1.com您会看到该 token 并验证:
    var ticket = FormsAuthentication.Decrypt(token);
    var userName = ticket.Name;
    var expires = ticket.Expiration;
    

    或与:
    var unprotected = MachineKey.Unprotect(Convert.FromBase64String(token), "auth");
    using (var ms = new MemoryStream(unprotected)) {
        var ticket = (MyTicket) new BinaryFormatter().Deserialize(ms);
        var user = ticket.Username;
    }
    
  • 您在 domain1.com 上创建 cookie使用您在 token 中收到的信息并将用户重定向回他最初来自的位置。

  • 所以有一堆重定向,但至少用户只需输入一次密码。

    更新以回答您的问题。
  • 是的,如果您发现该用户在 domain1.com 上通过了身份验证,您将重定向到 auth.domain.com。但是在 auth.domain.com 使用 token 重定向回来之后 - 您像往常一样在 domain1.com 上创建一个 cookie,并且用户登录到 domain1.com。所以这个重定向每个用户只发生一次(就像通常的登录一样)。
  • 您可以使用 javascript(XmlHttpRequest 或 jquery.get\pos​​t 方法)向 auth.domain.com 发出请求。但请注意,您必须配置 CORS 以允许这样做(例如,参见 here)。简而言之,CORS 是什么?当通过 javascript 从 siteA(另一个域)请求 siteB 时 - 浏览器将首先询问 siteB 是否信任 siteA 发出此类请求。它通过向请求添加特殊 header 来实现这一点,并且它希望看到一些特殊的 header 作为响应。您需要添加这些 header 以允许 domain1.com 通过 javascript 请求 auth.domain.com。完成后 - 从 domain1.com javascript 向 auth.domain.com 发出此类请求,如果已登录 - auth.domain.com 将如上所述返回您的 token 。然后使用该 token 对 domain1.com 进行查询(再次使用 javascript),以便 domain1.com 可以设置 cookie 作为响应。现在您已使用 cookie 登录 domain1.com 并可以继续。
    为什么我们需要所有这些,即使我们有一个应用程序只能从不同的域访问?因为浏览器不知道这一点并且对待它们完全不同。除此之外 - http 协议(protocol)是无状态的,每个请求都与其他请求无关,因此我们的服务器还需要确认请求 A 和 B 由同一用户发出,因此需要这些 token 。
  • 是的,HttpServerUtility.UrlTokenEncode在这里使用非常好,甚至比 Convert.ToBase64String 更好,因为无论如何您都需要对其进行 url 编码(您在查询字符串中传递它)。但是,如果您不会在查询字符串中传递 token (例如,您将使用上面的 javascript 方式 - 您不需要对它进行 url 编码,所以在这种情况下不要使用 HttpServerUtility.UrlTokenEncode
  • 关于c# - Multi-Tenancy ASP.NET MVC 应用程序的跨域 OWIN 身份验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36776606/

    相关文章:

    c# - 按位枚举强制转换返回值不是预期的

    c# - 垂直翻转字节数组中位图的算法

    .net - ASP :literal printing in final markup from kentico

    asp.net-mvc - AJAX 调用在 IIS 7.5 中返回 404(本地),但在其他 IIS 中同样有效

    c# - MVC4 Web API返回带有特殊字符的json propertykey字符串

    c# - Visual Studio 2015 - "Analyzers"引用是什么意思?

    c# - 如何从一年中的某一天获取日期

    c# - 如果是持久(保持事件)连接,为什么我会在 Web 请求中到达 endOfStream?

    asp.net - 登录控件 - 当前上下文中不存在控件

    css - 复选框顶部居中标签