6.6 Login Sessions - Page 11
July 12, 2002
Users expect web applications to behave as desktop programs: they log in once, and
that validation is good until they exit the system. Unfortunately, the nature of web
browsing doesn't mesh well with this, particularly in that " exit the system" part.
The typical web approximation is to keep a login session: the user is asked for validation
the first time he makes a request, and that validation is good as long as the user
remains active. If the user doesn't send a request for a certain time period— say, one
hour— the session expires and the user is " logged out" (even though in reality he was
never logged in).
This kind of validation checking should be unobtrusive, as described at the end
of the previous section. We won't require the user to start at a particular page, but
will detect when the user needs to log in; having done so, we'll serve up the
requested content.
The following example, Directory.pm, offers web access to a regular file directory.
While this is a very poor way to serve static content, it does demonstrate login
sessions and also how a mod_perl application can simulate a file directory (as described
in the previous chapter). The program is divided into two functions: handler
receives the requests and either calls serveDocument or displays the login form.
First let's look at handler:
sub handler {
# Get the request object and parse it.
my $r = shift;
my (%session, @ids, $username);
# Connect to the database.
my $dbh = DBI->connect('DBI:mysql:Info:localhost',
'web','nouser');
return error ($r, "Can't connect to database")
unless $dbh;
This is typical opening code. Most paths through the function will use the database,
so we open it here for simplicity. Remember that Apache:: DBI will be secretly caching
database connections, so in most cases this code will simply return a waiting
database handle.
Next we check for a session ID:
# Check for a cookie with the ID.
if (my $cookies = Apache::Cookie->fetch) {
my $cookieID = $cookies->{'ID'}->value
if $cookies->{'ID'};
push @ids, $cookieID if $cookieID;
}
This is the same sort of test we saw in other session examples. As in the previous
chapter, we'll check for the session ID both in a cookie and in the URL to support
browsers that don't manage cookies for us. The URL is checked for the session ID
when we look for the document name:
# Examine the URI; it may have a session id,
# and also probably has the requested file.
my $path = $r->uri;
if ($path =~ m{/protected/sessions/(\w+)(/.*)?}) {
# Session ID is in the URL.
push @ids, $1;
$path = $2;
}
elsif ($path =~ m{/protected/(.+)}) {
# Session ID (if any) will be in a cookie.
$path = $1;
}
else {
# Display the directory.
$path = '';
}
If there was a session ID in either, we check that it is a valid session and hasn't expired.
Note that this example uses Apache:: Session:: MySQL to store session info; see the
documentation on that module for how to create the required table in your database.
# Did we get a session ID somewhere?
if (@ids) {
my $inSession;
foreach my $try (@ids) {
eval {
tie %session, 'Apache::Session::MySQL', $try,
{Handle => $dbh, LockHandle => $dbh};
};
next if $@;
If eval noted an error then the session ID isn't valid. Some periodic task should run
through the Sessions table and remove data that is too old to be useful. The following
shows how this example uses expired sessions:
# Check the expiration.
if (time() - $session{'time'} < 3600) {
# Less than one hour, so we're good.
$inSession++;
last;
}
else {
# Get the username, then delete it.
$username = $session{'username'};
tied(%session)->delete;
undef(%session);
}
}
6.5 User Management - Page 10
Web Development with Apache and Perl
6.6 Login Sessions (Cont.) - Page 12
|