3 package Catalyst::Plugin::Session;
4 use base qw/Class::Accessor::Fast/;
10 use Catalyst::Exception ();
14 our $VERSION = "0.01";
17 __PACKAGE__->mk_accessors(qw/sessionid session_delete_reason/);
25 $c->check_session_plugin_requirements;
31 sub check_session_plugin_requirements {
34 unless ( $c->isa("Catalyst::Plugin::Session::State")
35 && $c->isa("Catalyst::Plugin::Session::Store") )
38 ( "The Session plugin requires both Session::State "
39 . "and Session::Store plugins to be used as well." );
42 Catalyst::Exception->throw($err);
49 my $cfg = ( $c->config->{session} ||= {} );
57 $c->NEXT::setup_session();
63 if ( $c->{session} ) {
65 # all sessions are extended at the end of the request
67 @{ $c->{session} }{qw/__updated __expires/} =
68 ( $now, $c->config->{session}{expires} + $now );
69 $c->store_session_data( $c->sessionid, $c->{session} );
72 $c->NEXT::finalize(@_);
78 if ( my $sid = $c->sessionid ) {
79 my $s = $c->{session} ||= $c->get_session_data($sid);
80 if ( !$s or $s->{__expires} < time ) {
83 $c->log->debug("Deleting session $sid (expired)") if $c->debug;
84 $c->delete_session("session expired");
86 elsif ($c->config->{session}{verify_address}
87 && $c->{session}{__address}
88 && $c->{session}{__address} ne $c->request->address )
91 "Deleting session $sid due to address mismatch ("
92 . $c->{session}{__address} . " != "
93 . $c->request->address . ")",
95 $c->delete_session("address mismatch");
98 $c->log->debug(qq/Restored session "$sid"/) if $c->debug;
102 $c->NEXT::prepare_action(@_);
106 my ( $c, $msg ) = @_;
108 # delete the session data
109 my $sid = $c->sessionid;
110 $c->delete_session_data($sid);
112 # reset the values in the context object
113 $c->{session} = undef;
114 $c->sessionid(undef);
115 $c->session_delete_reason($msg);
121 return $c->{session} if $c->{session};
123 my $sid = $c->generate_session_id;
126 $c->log->debug(qq/Created session "$sid"/) if $c->debug;
128 return $c->initialize_session_data;
131 sub initialize_session_data {
136 return $c->{session} = {
139 __expires => $now + $c->config->{session}{expires},
142 $c->config->{session}{verify_address}
143 ? ( __address => $c->request->address )
149 sub generate_session_id {
152 my $digest = $c->_find_digest();
153 $digest->add( $c->session_hash_seed() );
154 return $digest->hexdigest;
159 sub session_hash_seed {
162 return join( "", ++$counter, time, rand, $$, {}, overload::StrVal($c), );
167 sub _find_digest () {
169 foreach my $alg (qw/SHA-1 MD5 SHA-256/) {
171 my $obj = Digest->new($alg);
177 or Catalyst::Exception->throw(
178 "Could not find a suitable Digest module. Please install "
179 . "Digest::SHA1, Digest::SHA, or Digest::MD5" );
182 return Digest->new($usable);
189 $c->NEXT::dump_these(),
192 ? ( [ "Session ID" => $c->sessionid ], [ Session => $c->session ], )
205 Catalyst::Plugin::Session - Generic Session plugin - ties together server side
206 storage and client side tickets required to maintain session data.
210 use Catalyst qw/Session Session::Store::FastMmap Session::State::Cookie/;
212 sub add_item : Local {
213 my ( $self, $c ) = @_;
215 my $item_id = $c->req->param("item");
217 # $c->session is stored across requests, so
218 # other actions will see these values
220 push @{ $c->session->{items} }, $item_id;
222 $c->forward("MyView");
225 sub display_items : Local {
226 my ( $self, $c ) = @_;
228 # values in $c->session are restored
229 $c->stash->{items_to_display} =
230 [ map { MyModel->retrieve($_) } @{ $c->session->{items} } ];
232 $c->forward("MyView");
237 The Session plugin is the base of two related parts of functionality required
238 for session management in web applications.
240 The first part, the State, is getting the browser to repeat back a session key,
241 so that the web application can identify the client and logically string
242 several requests together into a session.
244 The second part, the Store, deals with the actual storage of information about
245 the client. This data is stored so that the it may be revived for every request
246 made by the same client.
248 This plugin links the two pieces together.
256 An accessor for the session ID value.
260 Returns a hash reference that might contain unserialized values from previous
261 requests in the same session, and whose modified value will be saved for future
264 This method will automatically create a new session and session ID if none
267 =item session_delete_reason
269 This accessor contains a string with the reason a session was deleted. Possible
286 This method is extended to also make calls to
287 C<check_session_plugin_requirements> and C<setup_session>.
289 =item check_session_plugin_requirements
291 This method ensures that a State and a Store plugin are also in use by the
296 This method populates C<< $c->config->{session} >> with the default values
297 listed in L</CONFIGURATION>.
301 This methoid is extended, and will restore session data and check it for
302 validity if a session id is defined. It assumes that the State plugin will
303 populate the C<sessionid> key beforehand.
307 This method is extended and will extend the expiry time, as well as persist the
308 session data if a session exists.
310 =item delete_session REASON
312 This method is used to invalidate a session. It takes an optional parameter
313 which will be saved in C<session_delete_reason> if provided.
315 =item initialize_session_data
317 This method will initialize the internal structure of the session, and is
318 called by the C<session> method if appropriate.
320 =item generate_session_id
322 This method will return a string that can be used as a session ID. It is
323 supposed to be a reasonably random string with enough bits to prevent
324 collision. It basically takes C<session_hash_seed> and hashes it using SHA-1,
325 MD5 or SHA-256, depending on the availibility of these modules.
327 =item session_hash_seed
329 This method is actually rather internal to generate_session_id, but should be
330 overridable in case you want to provide more random data.
332 Currently it returns a concatenated string which contains:
346 One value from C<rand>.
350 The stringified value of a newly allocated hash reference
354 The stringified value of the Catalyst context object
358 In the hopes that those combined values are entropic enough for most uses. If
359 this is not the case you can replace C<session_hash_seed> with e.g.
361 sub session_hash_seed {
362 open my $fh, "<", "/dev/random";
363 read $fh, my $bytes, 20;
368 Or even more directly, replace C<generate_session_id>:
370 sub generate_session_id {
371 open my $fh, "<", "/dev/random";
372 read $fh, my $bytes, 20;
374 return unpack("H*", $bytes);
377 Also have a look at L<Crypt::Random> and the various openssl bindings - these
378 modules provide APIs for cryptographically secure random data.
382 See L<Catalyst/dump_these> - ammends the session data structure to the list of
383 dumped objects if session ID is defined.
387 =head1 USING SESSIONS DURING PREPARE
389 The earliest point in time at which you may use the session data is after
390 L<Catalyst::Plugin::Session>'s C<prepare_action> has finished.
392 State plugins must set $c->session ID before C<prepare_action>, and during
393 C<prepare_action> L<Catalyst::Plugin::Session> will actually load the data from
399 # don't touch $c->session yet!
401 $c->NEXT::prepare_action( @_ );
403 $c->session; # this is OK
404 $c->sessionid; # this is also OK
409 $c->config->{session} = {
413 All configuation parameters are provided in a hash reference under the
414 C<session> key in the configuration hash.
420 The time-to-live of each session, expressed in seconds. Defaults to 7200 (two
425 When false, C<< $c->request->address >> will be checked at prepare time. If it
426 is not the same as the address that initiated the session, the session is
433 The hash reference returned by C<< $c->session >> contains several keys which
434 are automatically set:
440 A timestamp whose value is the last second when the session is still valid. If
441 a session is restored, and __expires is less than the current time, the session
446 The last time a session was saved. This is the value of
447 C<< $c->{session}{__expires} - $c->config->{session}{expires} >>.
451 The time when the session was first created.
455 The value of C<< $c->request->address >> at the time the session was created.
456 This value is only populated of C<verify_address> is true in the configuration.
462 C<verify_address> could make your site inaccessible to users who are behind
463 load balanced proxies. Some ISPs may give a different IP to each request by the
464 same client due to this type of proxying. If addresses are verified these
465 users' sessions cannot persist.
467 To let these users access your site you can either disable address verification
468 as a whole, or provide a checkbox in the login dialog that tells the server
469 that it's OK for the address of the client to change. When the server sees that
470 this box is checked it should delete the C<__address> sepcial key from the
471 session hash when the hash is first created.