Server side session management system might be seeming awfully convoluted if you have never dealt with
it. Fortunately, with CGI::Session all the complexity is handled by the library transparently. This
section of the manual can be treated as an introductory tutorial to both logic behind session
management, and to CGI::Session programming style.
All applications making use of server side session management rely on the following pattern of operation
regardless of the way the system is implemented:
1. Check if the user has session cookie dropped in his computer from previous request
2. If the cookie does not exist, create a new session identifier, and drop it as cookie to the user's
computer.
3. If session cookie exists, read the session ID from the cookie and load any previously saved session
data from the server side storage. If session had any expiration date set it's useful to re-drop the
same cookie to the user's computer so its expiration time will be reset to be relative to user's last
activity time.
4. Store any necessary data in the session that you want to make available for the next HTTP request.
CGI::Session will handle all of the above steps. All you have to do is to choose what to store in the
session.
GETTINGSTARTED
To make CGI::Session's functionality available in your program do either of the following somewhere on
top of your program file:
use CGI::Session;
# or
require CGI::Session;
Whenever you're ready to create a new session in your application, do the following:
$session = CGI::Session->new () or die CGI::Session->errstr;
Above line will first try to re-initialize an existing session by consulting cookies and necessary
QUERY_STRING parameters. If it fails will create a brand new session with a unique ID, which is normally
called sessionID, SID for short, and can be accessed through id() - object method.
We didn't check for any session cookies above, did we? No, we didn't, but CGI::Session did. It looked for
a cookie called "CGISESSID", and if it found it tried to load existing session from server side storage
(file in our case). If cookie didn't exist it looked for a QUERY_STRING parameter called "CGISESSID". If
all the attempts to recover session ID failed, it created a new session.
NOTE: For the above syntax to work as intended your application needs to have write access to your
computer's TEMPDIR folder, which is usually /tmp in UNIX. If it doesn't, or if you wish to store this
application's session files in a different place, you may pass the third argument like so:
$session = CGI::Session->new(undef, undef, {Directory=>'../tmp/sessions'});
Now it will store all the newly created sessions in (and will attempt to initialize requested sessions
from) that folder. Don't worry if the directory hierarchy you want to use doesn't already exist. It will
be created for you. For details on how session data are stored refer to CGI::Session::Driver::file, which
is the default driver used in our above example.
There is one small, but very important thing your application needs to perform after creating
CGI::Session object as above. It needs to drop Session ID as an HTTPcookie into the user's computer.
CGI::Session will use this cookie to identify the user at his/her next request and will be able to load
his/her previously stored session data.
To make sure CGI::Session will be able to read your cookie at next request you need to consult its name()
method for cookie's suggested name:
$cookie = $query->cookie( -name => $session->name,
-value => $session->id );
print $query->header( -cookie=>$cookie );
name() returns "CGISESSID" by default. If you prefer a different cookie name, you can change it as easily
too, but you have to do it before CGI::Session object is created:
CGI::Session->name("SID");
$session = CGI::Session->new();
Baking the cookie wasn't too difficult, was it? But there is an even easier way to send a cookie using
CGI::Session:
print $session->header();
The above will create the cookie using CGI::Cookie and will return proper http headers using CGI.pm's CGI
method. Any arguments to CGI::Session will be passed to CGI::header().
Of course, this method of initialization will only work if client is accepting cookies. If not you would
have to pass session ID in each URL of your application as QUERY_STRING. For CGI::Session to detect it
the name of the parameter should be the same as returned by name():
printf ("<a href=\"$ENV{SCRIPT_NAME}?%s=%s\">click me</a>", $session->name, $session->id);
If you already have session id to be initialized you may pass it as the only argument, or the second
argument of multi-argument syntax:
$session = CGI::Session->new( $sid );
$session = CGI::Session->new( "serializer:freezethaw", $sid );
$session = CGI::Session->new( "driver:mysql", $sid, {Handle=>$dbh} );
By default CGI::Session uses standard CGI to parse queries and cookies. If you prefer to use a different,
but compatible object you can pass that object in place of $sid:
$cgi = CGI::Simple->new();
$session = CGI::Session->new( $cgi );
$session = CGI::Session->new( "driver:db_file;serializer:storable", $cgi);
# etc
See CGI::Simple
STORINGDATA
CGI::Session offers param() method, which behaves exactly as CGI.pm's param() with identical syntax.
param() is used for storing data in session as well as for accessing already stored data.
Imagine your customer submitted a login form on your Web site. You, as a good host, wanted to remember
the guest's name, so you can a) greet him accordingly when he visits your site again, or b) to be helpful
by filling out username part of his login form, so the customer can jump right to the password field
without having to type his username again.
my $name = $cgi->param('username');
$session->param('username', $name);
Notice, we're grabbing username value of the field using CGI.pm's (or another compatible library's)
param() method, and storing it in session using CGI::Session's param() method.
If you have too many stuff to transfer into session, you may find yourself typing the above code over and
over again. I've done it, and believe me, it gets very boring too soon, and is also error-prone. So we
introduced the following handy method:
$session->save_param(['name']);
If you wanted to store multiple form fields just include them all in the second list:
$session->save_param(['name', 'email']);
If you want to store all the available QUERY_STRING parameters you can omit the arguments:
$session->save_param();
See save_param() for more details.
When storing data in the session you're not limited to strings. You can store arrays, hashes and even
most objects. You will need to pass them as references (except objects).
For example, to get all the selected values of a scrolling list and store it in the session:
my @fruits = $cgi->param('fruits');
$session->param('fruits', \@fruits);
For parameters with multiple values save_param() will do the right thing too. So the above is the same
as:
$session->save_param($cgi, ['fruits']);
All the updates to the session data using above methods will not reflect in the data store until your
application exits, or $session goes out of scope. If, for some reason, you need to commit the changes to
the data store before your application exits you need to call flush() method:
$session->flush();
I've written a lot of code, and never felt need for using flush() method, since CGI::Session calls this
method at the end of each request. There are, however, occasions I can think of one may need to call
flush().
ACCESSINGSTOREDDATA
There's no point of storing data if you cannot access it. You can access stored session data by using the
same param() method you once used to store them. Remember the Username field from the previous section
that we stored in the session? Let's read it back so we can partially fill the Login form for the user:
$name = $session->param("name");
printf "<input type=\"text\" name=\"name\" value=\"%s\" />", $name;
To retrieve previously stored @fruits do not forget to de reference it:
@fruits = @{ $session->param('fruits') };
Very frequently, you may find yourself having to create pre-filled and pre-selected forms, like radio
buttons, checkboxes and drop down menus according to the user's preferences or previous action. With text
and textareas it's not a big deal - you can simply retrieve a single parameter from the session and hard
code the value into the text field. But how would you do it when you have a group of radio buttons,
checkboxes and scrolling lists? For this purpose, CGI::Session provides load_param() method, which loads
given session parameters to a CGI object (assuming they have been previously saved with save_param() or
alternative):
$session->load_param($cgi, ["fruits"]);
Now when you say:
print $cgi->checkbox_group(fruits=>['apple', 'banana', 'apricot']);
See load_param() for details.
Generated checkboxes will be pre-filled using previously saved information.
If you're making use of HTML::Template to separate the code from the skin, you can as well associate
CGI::Session object with HTML::Template and access all the parameters from within HTML files. We love
this trick!
$template = HTML::Template->new(filename=>"some.tmpl", associate=>$session);
print $template->output();
Assuming the session object stored "first_name" and "email" parameters while being associated with
HTML::Template, you can access those values from within your "some.tmpl" file now:
Hello <a href="mailto:<TMPL_VAR email>"> <TMPL_VAR first_name> </a>!
See HTML::Template's online manual for details.
CLEARINGSESSIONDATA
You store session data, you access session data and at some point you will want to clear certain session
data, if not all. For this purpose CGI::Session provides clear() method which optionally takes one
argument as an arrayref indicating which session parameters should be deleted from the session object:
$session->clear(["~logged-in", "email"]);
Above line deletes "~logged-in" and "email" session parameters from the session. And next time you say:
$email = $session->param("email");
it returns undef. If you omit the argument to clear(), be warned that all the session parameters you ever
stored in the session object will get deleted. Note that it does not delete the session itself. Session
stays open and accessible. It's just the parameters you stored in it gets deleted
See clear() for details.
DELETINGASESSION
If there's a start there's an end. If session could be created, it should be possible to delete it from
the disk for good:
$session->delete();
The above call to delete() deletes the session from the disk for good. Do not confuse it with clear(),
which only clears certain session parameters but keeps the session open.
See delete() for details.
EXPIRATION
CGI::Session provides limited means to expire sessions. Expiring a session is the same as deleting it via
delete(), but deletion takes place automatically. To expire a session, you need to tell the library how
long the session would be valid after the last access time. When that time is met, CGI::Session refuses
to retrieve the session. It deletes the session and returns a brand new one. To assign expiration ticker
for a session, use expire():
$session->expire(3600); # expire after 3600 seconds
$session->expire('+1h'); # expire after 1 hour
$session->expire('+15m'); # expire after 15 minutes
$session->expire('+1M'); # expire after a month and so on.
When session is set to expire at some time in the future, but session was not requested at or after that
time has passed it will remain in the disk. When expired session is requested CGI::Session will remove
the data from disk, and will initialize a brand new session.
See expire() for details.
Before CGI::Session 4.x there was no way of intercepting requests to expired sessions. CGI::Session 4.x
introduced new kind of constructor, load(), which is identical in use to new(), but is not allowed to
create sessions. It can only load them. If session is found to be expired, or session does not exist it
will return an empty CGI::Session object. And if session is expired, in addition to being empty, its
status will also be set to expired. You can check against these conditions using empty() and is_expired()
methods. If session was loaded successfully object returned by load() is as good a session as the one
returned by new():
$session = CGI::Session->load() or die CGI::Session->errstr;
if ( $session->is_expired ) {
die "Your session expired. Please refresh your browser to re-start your session";
}
if ( $session->is_empty ) {
$session = $session->new();
}
Above example is worth an attention. Remember, all expired sessions are empty sessions, but not all empty
sessions are expired sessions. Following this rule we have to check with is_expired() before checking
with is_empty(). There is another thing about the above example. Notice how its creating new session when
un existing session was requested? By calling new() as an object method! Handy thing about that is, when
you call new() on a session object new object will be created using the same configuration as the
previous object.
For example:
$session = CGI::Session->load("driver:mysql;serializer:storable", undef, {Handle=>$dbh});
if ( $session->is_expired ) {
die "Your session is expired. Please refresh your browser to re-start your session";
}
if ( $session->is_empty ) {
$session = $session->new();
}
Initial $session object was configured with mysql as the driver, storable as the serializer and $dbh as
the database handle. Calling new() on this object will return an object of the same configuration. So
$session object returned from new() in the above example will use mysql as the driver, storable as the
serializer and $dbh as the database handle.
See is_expired(), is_empty(), load() for details.
Sometimes it makes perfect sense to expire a certain session parameter, instead of the whole session. I
usually do this in my login enabled sites, where after the user logs in successfully, I set his/her
"_logged_in" session parameter to true, and assign an expiration ticker on that flag to something like 30
minutes. It means, after 30 idle minutes CGI::Session will clear "_logged_in" flag, indicating the user
should log in over again. I agree, the same effect can be achieved by simply expiring() the session
itself, but by doing this we would loose other session parameters, such as user's shopping cart, session-
preferences and the like.
This feature can also be used to simulate layered authentication, such as, you can keep the user's access
to his/her personal profile information for as long as 60 minutes after a successful login, but expire
his/her access to his credit card information after 5 idle minutes. To achieve this effect, we will use
expire() method again:
$session->expire(_profile_access, '1h');
$session->expire(_cc_access, '5m');
With the above syntax, the person will still have access to his personal information even after 5 idle
hours. But when he tries to access or update his/her credit card information, he may be displayed a
"login again, please" screen.
See expire() for details.
This concludes our discussion of CGI::Session programming style. The rest of the manual covers some
"SECURITY" issues. Driver specs from the previous manual were moved to CGI::Session::Driver.