Forms Authentication User.IsInRole() randomly not

2019-06-14 18:36发布

I was changing the default LogOn function in an MVC3 project to redirect a user to a certain page based on their role by using User.IsInRole(). When I was testing this out, the first couple users redirected as expected, but after that I had a couple that didn't redirect to where they are supposed to (they passed through all the statements and hit the default home index.) It seems completely random, sometimes my admin will be taken to the admin page, other times not.

My LogOn function:

[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
    if(ModelState.IsValid)
    {
        if(Membership.ValidateUser(model.UserName, model.Password))
        {
            FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
            if(Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && 
                returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && 
                !returnUrl.StartsWith("/\\"))
            {
                return Redirect(returnUrl);
            }
            else
            {
                if(User.IsInRole("Admin") || User.IsInRole("SuperAdmin"))
                {
                    return RedirectToAction("Index", "Admin");
                }
                else if(User.IsInRole("Employee"))
                {
                    return RedirectToAction("Index", "Employee");
                }
                else if(User.IsInRole("Accounting"))
                {
                    return RedirectToAction("Index", "Accounting");
                }
                // If the user is in none of those roles, send them to the home index
                return RedirectToAction("Index", "Home");
            }
        }
        else
        {
            MembershipUser user = Membership.GetUser(model.UserName);
            if(user == null)
                ModelState.AddModelError("", "The user name or password provided is incorrect.");
            else
                ModelState.AddModelError("", "You haven't been approved yet, or you are locked out.");
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}

Looking at the IntelliTrace, it seems like sometimes it doesn't bother to query the database, for example, when it works I see Execute Reader "dbo.aspnet_UsersInRoles_GetRolesForUser" and when it doesn't I don't.

Does anyone know why User.IsInRole() would return false even if the user is in the role? Is there some form of cashing happening, why isn't it queering the database every time?

I know for sure the users are in the roles I am testing, and I know it is not trying to redirect to the return url, I also know that the role isn't being stored in any cookies. Any ideas would be appreciated, I'm sure I could go about this another way, however now I'm more interested in why this simple approach isn't working.

Update

I found that if i replace the If(User.IsInRole(... statements with a redirect to another action called sorting, and I add the if statements there, it works 100% of the time.

public ActionResult Sorting()
{
    if(User.IsInRole("Admin") || User.IsInRole("SuperAdmin"))
    {
        return RedirectToAction("Index", "Admin");
    }
    else if(User.IsInRole("Employee"))
    {
        return RedirectToAction("Index", "Employee");
    }
    else if(User.IsInRole("Accounting"))
    {
        return RedirectToAction("Index", "Accounting");
    }
    // If the user is in none of those roles, send them to the home index
    return RedirectToAction("Index", "Home");
}

So apparently User.Identity.Name is not set (or will not return the user's name) until the LogOn function exits. Is this correct? I figured that after the Membership.ValidateUser was called the user was authenticated, apparently not.

So at what point after Membership.ValidateUser() is called, will the User.IsInRole() going to work correctly? Is is after the cookie is dropped, or what?

I suppose I could use if(Roles.IsUserInRole(model.UserName, "Admin")) since I do have the user's name from the model submitted. Do you think this is a better idea or just use the Sorting redirect like I did?

1条回答
【Aperson】
2楼-- · 2019-06-14 18:52

The problem is that when the LogOn function is called, the incoming request has no authenticated user, the User object is null, it is not until a new request is made that the User object is populated. So when User.IsInRole() is called in the LogOn, User is null.

In short, the User object is set earlier in the ASP.NET pipeline, long before the requested ASP.NET page's code is executed. Now, on the subsequent visit, the ASP.NET runtime will see the forms authentication ticket and User.Identity.IsAuthenticated will be true, but not on this request.
...

Also, you won't be able to get the username from User.Identity.Name. Instead, use LoginControlID.Username.

http://forums.asp.net/post/1988606.aspx

查看更多
登录 后发表回答