A user session could be defined as the requests that a single client makes to a server in the course of a visit to an application. The user session begins when he or she makes the first request and ends (theoretically) when he or she logs out of the application or when he or she closes the browser. If a user leaves the application to surf to another web site, the session has not actually ended, but it can extend until the session-identifying variables have timed out.
Session variable timeouts are set in the ColdFusion Administrator to default to 20 minutes. This means that after 20 minutes of inactivity, the user session ends. The default timeout can be overridden by creating a timeout for the session in the CFAPPLICATION tag of the Application.cfm template of the application.
New to ColdFusion MX, the ColdFusion Administrator gives us two options for session management:
ColdFusion session management uses two variable values to identify the session. Those variables are CFID and CFTOKEN. ColdFusion creates a space in the server's memory to hold the values of those two session variables and sets cookies on the client machine with corresponding values. It uses these to track the activity of the session. These values are combined with the application name to form a session ID.
J2EE session management uses a user/session identifier that is created for every session. That identifier is called the jsessionid. J2EE session management should be used if you will be calling CFML templates, Java Server Pages (JSP) pages, or Java Servlets within the same application.
Understanding Session Variables
Session variables, as their name implies, exist for the life of the user session. You might think that would mean that they exist until the user logs off or closes his or her browser. Well, in a perfect world that might be true, but as we know, this is not such a world.
Session variables are variables that relate to a specific user session. They are not available across sessions and are not available to other users. Session variables are great for tracking a logged-in user. They are also great for setting a simple value that identifies that he or she is logged in. It can also hold his or her user ID or email address.
Session variables are not perfect, however. They are not the answer to all your problems. One of the biggest drawbacks to session variables is that because they are stored in the memory of the server that created them, using session variables within an application that is deployed in a clustered environment often does not work.
The problem is that when a user logs in to a specific server in your cluster, the session variable is set in the server memory and a corresponding cookie is sent to the client machine. If a load gets heavy on the server into which the user logged, their subsequent requests might be moved to a server with a lesser load. This results in the loss of user information, preferences, and other session information.
The only remedy for this situation is to employ "sticky sessions" after a user logs in. This can be accomplished programmatically by specifying the server into which the user is logged in the URL for any subsequent requests. More often, however, sticky sessions are enabled within the load-balancing solution. That's a discussion for another chapter.
Enabling Session Variables
There are two basic steps to enabling the use of session variables in your application. As mentioned, the sessionmanagement attribute of the CFAPPLICATION tag must be set to YES to use session variables. Within the ColdFusion Administrator, you must also enable the use of session variables within the Memory Variables section. By default, session variables are enabled in ColdFusion MX.
Using Session Variables
We've already defined a user session as starting with a first request to the application. When that first request is made, ColdFusion creates several session variables. If you are using ColdFusion session management, ColdFusion creates CFID, CFToken, SessionID, and URLToken. If you have enabled J2EE session management, ColdFusion creates SessionID and URLToken.
Session variables are stored in server memory and, as such, are lost whenever that server is restarted. Therefore, it makes the most sense to use session variables to remember information pertaining to the current user session only. When that session ends, the information can be removed from the server memory without interrupting the user's visit to the application.
Let's take a look at an example of code from an application that requires a login. The code is checking for the existence of a session variable called Session.LoggedIn.
<!------------- Template: Login.cfm Author: Neil Ross (email@example.com) Date: 03/01/2002 Sample simple login page. --------------> <form name="loginform" action="authenticate.cfm" method="post"> <table> <cfif IsDefined("url.message")> <tr> <td colspan="2"><cfoutput>#url.message#</cfoutput></td> </tr> </cfif> <tr> <td colspan="2"> Please provide your email address and password below. </td> </tr> <tr> <td>Email: </td> <td><input name="email" type="text"></td> </tr> <tr> <td>Password: </td> <td><input name="password" type="password"></td> </tr> <tr> <td></td> <td><input type="submit" value="Log In"></td> </tr> </table> </form>
The authentication template might look something like this:
<!------------- Template: Authenticate.cfm Author: Neil Ross (firstname.lastname@example.org) Date: 03/01/2002 Sample login authentication page which sets a session variable with a value of the email address. --------------> <cfif IsDefined("form.email") AND form.email IS NOT "" AND IsDefined("form.password") AND form.password is not ""> <cfquery name="AuthenticateUser" datasource=request.dsn> SELECT UserID FROM Users WHERE Email = '#form.email#' AND Password = '#form.password#' </cfquery> <cfif AuthenticateUser.RecordCount IS 1> <cflock scope="session" type="exclusive" timeout="10"> <cfset session.CurrentUser=form.email> </cflock> <cfset variables.WelcomeMessage="Welcome to InsideColdFusionMX.com."> <cflocation url="/index.cfm?Message=#URLEncodedFormat(variables.WelcomeMessage)#"> <cfelse> <cfset variables.FailureMessage="Your username or password is incorrect. Please try to log in again."> <cflocation url="login.cfm?Message=# URLEncodedFormat(variables.FailureMessage)#"> </cfif> </cfif>
We can see from the example above that session variables are created using a simple CFSET call:
<cflock scope="session" type="exclusive" timeout="10"> <cfset session.CurrentUser=form.email> </cflock>
They can also be created using the CFPARAM tag:
<cflock scope="session" type="exclusive" timeout="10"> <cfparam name="session.CurrentUser" default="#form.email#"> </cflock>
The CFPARAM tag also effectively checks for the existence of the variable named prior to setting it and sets it only if the variable does not exist.
Similarly, a session variable can be deleted by using a simple and familiar structure call:
<cflock scope="Session" type="Exclusive" timeout="10"> <cfset temp=StructDelete(Session, "CurrentUser")> </cflock>
Ending a User Session
We said earlier that a user's session ends when he or she stops making requests to the application. However, it is difficult for the application to tell whether the user has finished making requests or if he or she is simply pausing. We also know that the session ends when the user has remained inactive for a period equal to or greater than the established session timeout.
If you are using J2EE session management, the session and all the associated session variables should be deleted when the user closes the browser. If you're using ColdFusion session management, the variables and their values continue to persist in the server memory until they have timed out or have been manually deleted.
You might want to end the user session programmatically. This might be through the user clicking a logout link somewhere within the application display. Try the code below to manually end the session:
<cflock scope="Session" type="Exclusive" timeout="10"> <cfset temp=StructClear(Session)> </cflock>
Well, this works great if the user actually clicks the link to log out. Most users, however, simply close the browser, so we have to build our applications to fit this scenario.
The session variables that identify the session are, of course, CFID and CFToken. These values are stored in the ColdFusion Server's memory. They are also set as cookies on the client machine. When ColdFusion checks the values in memory against the cookies on the client machine and does not find a match, the session is officially ended. The cookies that ColdFusion sets on the client machine do not expire when the browser is closed. They are persistent. For this reason, we often find that our session is still open even after we've closed our browser and restarted it.
To end the session when the user closes his or her browser, we must reset the expiration on the CFID and CFTOKEN cookies. The following code shows you how to do just that by setting the existing CFID and CFTOKEN values to local variables and then resetting the cookies:
<cfif IsDefined("cookie.CFID") AND IsDefined("cookie.CFToken"> <cfset variables.CFID=cookie.CFID> <cfset variables.CFToken=cookie.CFToken> <cfcookie name="CFID" value=variables.CFID> <cfcookie name="CFToken" value=variables.CFToken> </cfif>
This is an effective solution, but only when all browsers that share those session attributes are closed. Thus, the user must close the active browser session and any child sessions that were spawned after the session variables were set.