Reputation: 59
long time lurker first time poster..
trying to implement a simple custom role and membership provider for MVC.
Have implemented the role and membership provider classes and hooked them into my web.config. added code to validate my users against a custom database and it works fine.
However, I dont like the way my roleprovider hits the database on each request so I added some code to try and read this from the authentication ticket as follows:
custom role provider:
public override string[] GetRolesForUser(string username)
{
if (HttpContext.Current.User != null)
{
return ((UserPrincipal)HttpContext.Current.User).Roles.ToArray();
}
else
{
UserPrincipal user = orchestrator.GetUserByLoginID(username);
return user.Roles.ToArray();
}
}
then I added this code in global.asax to persist the roles and some other useful user info into the cookie using a custom user principal object:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
UserPrincipalModel userFromTicket = serializer.Deserialize<UserPrincipalModel>(authTicket.UserData);
UserPrincipal newUser = new UserPrincipal();
newUser.UserId = userFromTicket.UserId;
newUser.FullName = userFromTicket.Fullname;
newUser.Email = userFromTicket.Email;
newUser.Roles = userFromTicket.Roles;
newUser.Identity = new GenericIdentity(userFromTicket.Username);
HttpContext.Current.User = newUser;
}
}
my user principal class:
public class UserPrincipal : IPrincipal
{
public UserPrincipal() { }
public UserPrincipal(int userId, string userName, string fullName, string password)
{
UserId = userId;
UserName = userName;
FullName = fullName;
Password = password;
}
public virtual int UserId { get; set; }
public virtual string UserName { get; set; }
public virtual string FullName { get; set; }
public virtual string Email { get; set; }
public virtual string Password { get; set; }
public virtual IEnumerable<string> Roles { get; set; }
public virtual IIdentity Identity { get; set; }
public virtual bool IsInRole(string role)
{
if (Roles.Contains(role))
{
return true;
}
else
{
return false;
}
}
//public string[] GetRolesForUser()
//{
// return Roles;
//}
}
However when i run this I get the following error when the role provider tries to access the custom UserPrincipal object in the cookie
"Unable to cast object of type 'System.Web.Security.RolePrincipal' to type 'MyApp.Domain.UserPrincipal'"
Its like the custom role provider is overwriting the custom user principal im storing in the ticket with its own Role specific principal.
Just wondering if what I'm trying to do is flawed or if theres an easier way. Dont want to reinvent the wheel.
Anyone got an example of a custom role provider that does not hit the db on each request for roles?
Upvotes: 2
Views: 2643
Reputation: 5299
Change this right here:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
To this right here:
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
Basically the inner workings of the default role provider are setting the HttpContext.Current.User property after you set it, thus overwriting what you did.
You can also try and clear the default role provider, it seems to work in mvc3 and asp.net web forms but fails in mvc 4.
<membership>
<providers>
<clear />
</providers>
</membership>
<profile>
<providers>
<clear />
</providers>
</profile>
<roleManager enabled="false">
<providers>
<clear />
</providers>
</roleManager>
Upvotes: 1