Sometimes you want to save more data into the authentication cookie rather than retrieve them from database all the time.
So I come up this way to create a custom Identity to allow you retrieve extra data from authentication cookies.
1. To create an identity, you need to implement the interface IIdentity.
I added Email & Roles as properties of the MyIdentity. And retrieve the data from FormsAuthenticationTicket.UserData (which is passed into MyIdentity’s constructor)
public class MyIdentity : IIdentity { private System.Web.Security.FormsAuthenticationTicket ticket; public MyIdentity(System.Web.Security.FormsAuthenticationTicket ticket) { this.ticket = ticket; } public string AuthenticationType { get { return "MobileOrder"; } } public bool IsAuthenticated { get { return true; } } public string Name { get { return ticket.Name; } } public string Email { get { string[] data = ticket.UserData.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); if (data.Length > 0) return data[0]; else return ""; } } public string[] Roles { get { string[] data = ticket.UserData.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); if (data.Length > 1) { string[] roles = data[1].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); return roles; } else return new string[] { "User" }; } } }
2. I created an AuthenticationHelper to create the cookie and assign the extra data to the Ticket’s UserData.
public class AuthenticationHelper { public static void CreateAuthCookie(int id, string email, DateTime expiration, string[] roles, bool rememberMe) { FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, //version id.ToString(), // user name DateTime.Now, expiration, //Expiration rememberMe, //Persistent CreateAuthTags(email, roles)); string encTicket = FormsAuthentication.Encrypt(authTicket); HttpContext.Current.Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)); } public static string CreateAuthTags(string email, string[] roles) { StringBuilder sb = new StringBuilder(); sb.Append(email); sb.Append("|"); for (int i = 0; i < roles.Length; i++) { sb.Append(roles[i]); if (i < roles.Length - 1) sb.Append(","); } return sb.ToString(); } }
3. In Global.asax, register MvcApplication_PostAuthenticateRequest event to cast generic Identity to MyIdentity and assign back to the Principal in HttpContext.Current.User
public override void Init() { this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest); base.Init(); } void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e) { HttpCookie authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName]; if (authCookie != null) { string encTicket = authCookie.Value; if (!String.IsNullOrEmpty(encTicket)) { FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(encTicket); MyIdentity id = new MyIdentity(ticket); GenericPrincipal prin = new GenericPrincipal(id, id.Roles); HttpContext.Current.User = prin; } } }
4. To read the MyIdentity from your pages or views
((MyIdentity)HttpContext.Current.User.Identity).Email;
Alternatively you can create PartialView for the login status and put it in _Layout.cshtml. eg.
@if(Request.IsAuthenticated) { <ul> <li class="small">@(((Mobile.Order.Web.Common.Identity.MyIdentity)HttpContext.Current.User.Identity).Email)</li> | <li>@Html.ActionLink("Account", "Index", "Account")</li> | <li>@Html.ActionLink("Log Off", "LogOff", new { controller = "Account", returnUrl = HttpContext.Current.Request.RawUrl })</li> </ul> } else { <ul> <li> @*@Html.ActionLink("Login", "Login", "Account")*@ <a href="/Account/AjaxLogin" id="loginButton">Login</a> </li> | <li>@Html.ActionLink("Register", "Index", "Register")</li> </ul> }