ASP.NET跨子域单点登录Cookie

时间:2010-10-28 20:26:55

标签: c# asp.net asp.net-mvc-2 single-sign-on

我有一个单点登录解决方案,它在我们的测试环境中运行良好。它使用跨子域cookie在两个Web应用程序之间共享身份验证票证。登录仅在其中一个应用程序上完成,在第二个站点上,用户通过第一个站点创建的cookie进行身份验证。

问题在于,当我将其滚动到生产环境中时,单点登录不再有效。我正在寻找有关为什么会这样的想法。更多详情如下:

1)这两个应用程序都是使用ASP.NET MVC2

实现的

2)在我们的测试环境中,两个网站都位于一台服务器上,IIS中有不同的网站,主机头用于为两个Web应用程序提供服务。在生产中,站点位于地理上分离的不同服务器上。但它们使用相同的子域。

3)两个站点都有SSL设置并通过https访问;这是在测试和生产中使用相同的自签名通配符证书完成的。

4)用户登录到site1,然后应用程序使用AJAX和JSONP自动从site2检索数据。

5)如果网站是site1.example.com和site2.example.com,则以下是site1和site2之间共享的web.config的重要部分:

...
<authentication mode="Forms">
  <forms name=".myapp" domain=".example.com" slidingExpiration="true" loginUrl="~/Account/LogOn" timeout="30"/>
</authentication>
...
<machineKey validationKey="KEY1..." decryptionKey="KEY2..."
  validation="SHA1" decryption="AES" />
...

注意:我对以上内容感到疑惑的一件事是,身份验证票据是否以只能在同一服务器上解密的方式加密\哈希?这可以解释我的问题;但如果是这种情况,我如何确保site1和site2的服务器都可以解密我的身份验证cookie? KEY1和KEY2在两个站点\服务器上都是一样的。

6)在Site1上,用户所在的角色也会使用以下内容插入到cookie中:

public ActionResult LogOn(string userName, string password, bool rememberMe, string returnUrl)
    {
        if (!ValidateLogOn(userName, password))
        {
            ViewData["rememberMe"] = rememberMe;
            return View(new SiteViewModel(this));
        }

        FormsAuth.SignIn(userName, rememberMe);

        // Add roles to cookie
        string[] roles = Roles.GetRolesForUser(userName);
        HttpCookie cookie = FormsAuthentication.GetAuthCookie(User.Identity.Name, rememberMe);
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
        // Store roles inside the Forms cookie.  
        FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(ticket.Version, userName,
            ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, String.Join("|", roles), ticket.CookiePath);
        cookie.Value = FormsAuthentication.Encrypt(newticket);
        cookie.HttpOnly = false;
        Response.Cookies.Remove(cookie.Name);
        Response.AppendCookie(cookie);

        if (!String.IsNullOrEmpty(returnUrl))
        {
            return Redirect(returnUrl);
        }
        return RedirectToAction("Index", "Home");
    }

7)使用以下内容在Site2上恢复角色:

        protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        if (Context.Request.IsAuthenticated)
        {
            FormsIdentity ident = (FormsIdentity)Context.User.Identity;
            string[] arrRoles = ident.Ticket.UserData.Split(new[] { '|' });
            Context.User = new System.Security.Principal.GenericPrincipal(ident, arrRoles);
        }
    }

编辑:

8)这是两台服务器上应用程序引用的程序集的打印输出:

        mscorlib: 2.0.0.0
    System: 2.0.0.0
        System.Configuration: 2.0.0.0
            System.Xml: 2.0.0.0
    System.ComponentModel.DataAnnotations: 3.5.0.0
        System.Core: 3.5.0.0
    System.Data: 2.0.0.0
        System.EnterpriseServices: 2.0.0.0
            System.Transactions: 2.0.0.0
    System.Data.Entity: 3.5.0.0
        System.Runtime.Serialization: 3.0.0.0
            SMDiagnostics: 3.0.0.0
    System.Web: 2.0.0.0
        System.Drawing: 2.0.0.0
        System.Web.RegularExpressions: 2.0.0.0
        System.Web.Services: 2.0.0.0
    System.Web.Abstractions: 3.5.0.0
    System.Web.Extensions: 3.5.0.0
        System.Data.Linq: 3.5.0.0
            System.Xml.Linq: 3.5.0.0
        System.ServiceModel: 3.0.0.0
            System.IdentityModel: 3.0.0.0
        System.ServiceModel.Web: 3.5.0.0
    System.Web.Mvc: 1.0.0.0
        System.Web.Routing: 3.5.0.0
    xVal: 1.0.0.0

1 个答案:

答案 0 :(得分:4)

是。加密是特定于服务器的。或者更精确地依赖于机器密钥是相同的。

  1. 在两个网络配置中尝试使用相同的机器密钥
  2. 如果那剂量有帮助的话。 删除加密,然后尝试
  3. 如果在完全删除安全性/加密后它不起作用,那么对于不兼容的加密没有任何结果。请告诉我们。

    <强>更新 这是从我们的web.config中提取的。当然删除网站细节后。尝试明确指定所有内容,尤其是路径

    <forms name=".ASPNET" protection="All" loginUrl="~/Account/LogOn" timeout="2880"
            path="/" domain=".example.com"/>