Using Cookies for Client State Management


The last technique that we’ll examine for managing client state is the cookie, which you learned about briefly earlier in the chapter. Unbeknownst to many Web developers, cookies are not an approved standard, although all major browsers support them and all Web application development technologies use them. To review, cookies are small state bags that belong to a particular domain and are stored on the client’s machine rather than on the server. ASP.NET utilizes cookies for two tasks:

  • Session state The associated cookie is .ASPXSession. The cookie stores the SessionID used to associate the request with its session data.

  • Forms authentication The associated cookie is .ASPXAUTH. The cookie stores encrypted credentials. Credentials can be decrypted and the user re-authenticated.

You can view all the cookies on your system by opening Microsoft Internet Explorer and selecting Tools\Internet Options to open the Internet Options dialog box. Click the Settings button to open the Settings dialog box. Click the View Files button to open Explorer and access your temporary Internet files directory. You can then sort by type Text Document or by items named Cookie. As you can see, you’ve got lots of cookies!

Cookies are actually a great way to manage state if you can guarantee that your clients use them. They can store multiple name/value combinations as long as the value is of type string (or can be converted to string). The only limitation with cookies is the amount of data that can be stored; most browsers support a maximum cookie size of 4 KB (4096 bytes, to be more precise).

Working with cookies in ASP.NET is simple. We use them in many of our sample applications, including the www.asp.net Web site, in which we store the roles that a user belongs to. Rather than fetching the user roles on each request from the database, we fetch the user roles only if a specific UserRoles cookie doesn’t exist. We then create the UserRoles cookie and add the roles the user belongs to. On subsequent requests, we can simply open the UserRoles cookie, extract the roles, and add them to the roles the current user belongs to. The following code fragment illustrates this.

//*********************************************************************
//
// Application_AuthenticateRequest Event
//
// If the client is authenticated with the application, then determine
// which security roles he/she belongs to and replace the "User" intrinsic
// with a custom IPrincipal security object that permits "User.IsInRole"
// role checks within the application
//
// Roles are cached in the browser in an in-memory encrypted cookie.
// If the cookie doesn’t exist yet for this session, create it.
//
//*********************************************************************
void Application_AuthenticateRequest(Object sender, EventArgs e) {
String[] roles = null;

if (Request.IsAuthenticated == true) {
// Create roles cookie if it doesn’t exist yet for this session.
if ((Request.Cookies["userroles"] == null) ||
(Request.Cookies["userroles"].Value == "")) {

// Get roles from UserRoles table, and add to cookie
roles = UserRoles.GetUserRoles(User.Identity.Name);

CreateRolesCookie(roles);

} else {

// Get roles from roles cookie
FormsAuthenticationTicket ticket =
FormsAuthentication.Decrypt(
Context.Request.Cookies["userroles"].Value);

// Ensure the user logged in and the user
// the cookie was issued to are the same
if (ticket.Name != Context.User.Identity.Name) {

// Get roles from UserRoles table, and add to cookie
roles = UserRoles.GetUserRoles(User.Identity.Name);

CreateRolesCookie(roles);
} else {
// convert the string representation of the role
// data into a string array
ArrayList userRoles = new ArrayList();

foreach (String role in
ticket.UserData.Split( new char[] {‘;’} )) {
userRoles.Add(role);
}

roles = (String[]) userRoles.ToArray(typeof(String));
}
}

// Add our own custom principal to the request
// containing the roles in the auth ticket
Context.User = new GenericPrincipal(Context.User.Identity, roles);
}
}


//*********************************************************************
//
// CreateRolesCookie
//
// Used to create the cookie that store the roles for the current
// user.
//
//*********************************************************************
private void CreateRolesCookie(string[] roles) {

// Create a string to persist the roles
String roleStr = "";
foreach (String role in roles) {
roleStr += role;
roleStr += ";";
}

// Create a cookie authentication ticket.
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // version
Context.User.Identity.Name, // user name
DateTime.Now, // issue time
DateTime.Now.AddHours(1), // expires every hour
false, // don’t persist cookie
roleStr // roles
);

// Encrypt the ticket
String cookieStr = FormsAuthentication.Encrypt(ticket);

// Send the cookie to the client
Response.Cookies["userroles"].Value = cookieStr;
Response.Cookies["userroles"].Path = "/";
Response.Cookies["userroles"].Expires = DateTime.Now.AddMinutes(5);

}

The first method, Application_AuthenticateRequest, is an event delegate that gets called when ASP.NET is ready to authenticate the request. Within this method, we check to see whether we have a cookie named UserRoles and whether it has a value.

If the cookie isn’t found, we load the roles for the user and then call the CreateRolesCookie method, passing in a string[] of role names. Within CreateRolesCookie, we simply format the string[] into a semicolon-delimited string, encrypt it using APIs from Forms Authentication, and then store the encrypted data in the UserRoles cookie.

If the UserRoles cookie is found, we first decrypt the value of the cookie, ensure that the user the cookie belongs to is the same user that is currently logged in, split the roles using a semicolon as the delimiter, and finally create a new GenericPrinicpal (authenticated identity), passing in the roles as one of the arguments.

Obviously this code works on each request, but it doesn’t go to the database on each request to refetch the roles. The www.asp.net site averages about 85,000 unique users per day. If each user made an average of 30 requests, by using cookies for storing the user roles, we would eliminate at least 2,465,000 requests to the database!




Microsoft ASP. NET Coding Strategies with the Microsoft ASP. NET Team
Microsoft ASP.NET Coding Strategies with the Microsoft ASP.NET Team (Pro-Developer)
ISBN: 073561900X
EAN: 2147483647
Year: 2005
Pages: 144

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net