ASP.NET MVC 单点登录和角色

8

我已经实现了基本的单点登录,跨越了两个MVC站点(称为SiteA和SiteB),使用以下方法之一:

http://forums.asp.net/p/1023838/2614630.aspx

他们位于同一域名的子域上,并在web.config中共享哈希\加密密钥等。我已经修改了cookie,使其可以被同一域名下的所有站点访问。所有这些似乎都正常工作。
这些网站位于不同的服务器上,没有访问相同的SQL数据库,因此只有SiteA实际上持有用户登录详细信息。SiteB具有成员数据库,但用户为空。
这对我的要求场景非常有效:
1)用户登录SiteA
2)应用程序通过AJAX从SiteA(使用JSONP)和SiteB(使用JSONP)加载数据
我在我的AccountController上有以下LogOn Action,这是“魔法”发生的地方:
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
    if (ModelState.IsValid)
    {
        if (MembershipService.ValidateUser(model.UserName, model.Password))
        {
            FormsService.SignIn(model.UserName, model.RememberMe);

            //modify the Domain attribute of the cookie to the second level of domain
            // Add roles  
            string[] roles = Roles.GetRolesForUser(model.UserName);
            HttpCookie cookie = FormsAuthentication.GetAuthCookie(User.Identity.Name, false);
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
            // Store roles inside the Forms cookie.  
            FormsAuthenticationTicket newticket = new FormsAuthenticationTicket(ticket.Version, model.UserName, 
                ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, String.Join("|", roles), ticket.CookiePath);
            cookie.Value = FormsAuthentication.Encrypt(newticket);
            cookie.HttpOnly = false;
            cookie.Domain = ConfigurationManager.AppSettings["Level2DomainName"];
            Response.Cookies.Remove(cookie.Name);
            Response.AppendCookie(cookie);

            if (!String.IsNullOrEmpty(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
    }

这段代码执行了一些我在初始情况下并不严格需要的操作,但与我的问题相关。它将用户在登录 SiteA 时的角色列表插入到身份验证票证的 UserData 中。然后,在 global.asax 中通过以下代码“恢复”该信息以在 SiteB 上使用:
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);
    }
}

所有上述内容都能正常工作,直到我将角色加入其中。如果我只在SiteB的控制器/操作上使用[Authorize]属性,则一切正常。但是,一旦我添加了[Authorize(roles="TestAdmin")],用户就无法访问该控制器操作。显然,我已经将用户添加到TestAdmin角色。
如果我在SiteB的global.asax代码中调试,离开global.asax代码时看起来还好,但当我在控制器本身中命中断点时,Controller.User和Controller.HttpContext.User现在是没有设置角色的System.Web.Security.RolePrincipal。
因此,我的问题是:有人有任何想法如何恢复SiteB上的角色或另一种方法来做到这一点吗?
2个回答

2

由于这个问题似乎已经停滞不前,我可以通过一些额外的发现部分回答这个问题。在进行了一些调试和测试后,似乎 MVC2 在离开 Application_AuthenticateRequest 但进入我的控制器之前做了一些奇怪的事情。更多细节请参见:

http://forums.asp.net/t/1597075.aspx

一个解决方法是使用 Application_AuthorizeRequest 代替 Application_AuthenticateRequest。

编辑:我相信我找到了问题的原因。在我的 MVC1 项目中,roleManager 被禁用了,但是我的测试 MVC2 项目启用了 roleManager。一旦我在 MVC1 测试项目中启用了 roleManager,MVC1 和 MVC2 之间的行为就是相同的。我还向 MVC 微软团队提出了一个问题,并将在此处发布回复,以确定 Application_AuthorizeRequest 是否是从身份验证票证 cookie 恢复角色的正确位置...


2
你已经解决了这个问题,但是我还是给你提供一下翻译: 让它工作:关闭角色管理器。这并不是asp.net的奇怪行为,因为你明确告诉它使用指定的配置来查找用户的角色。 另一种方法:在两个应用程序中启用角色管理器。像在你的自定义代码中那样使用配置来共享cookie。根据你的描述,只要使用匹配的身份验证cookie配置,你就不需要担心它会尝试获取用户的角色。
你应该使用Application_AuthorizeRequest来设置角色cookie吗?在我看来,更早的Authenticate是最好的选择,我一直都是这样做的,从未遇到过问题。

谢谢。是的,我已经大部分弄清楚了,但确认一下还是很好的。 :) 如果您能给出一些Application_AuthenticateRequest比Application_AuthorizeRequest更好的原因,我将很高兴地接受这个答案。 - mutex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接