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 my $ret = $c->NEXT::prepare_action;
80 my $sid = $c->sessionid || return;
82 $c->log->debug(qq/Found session "$sid"/) if $c->debug;
84 my $s = $c->{session} ||= $c->get_session_data($sid);
85 if ( !$s or $s->{__expires} < time ) {
88 $c->log->debug("Deleting session $sid (expired)") if $c->debug;
89 $c->delete_session("session expired");
93 if ( $c->config->{session}{verify_address}
94 && $c->{session}{__address}
95 && $c->{session}{__address} ne $c->request->address )
98 "Deleting session $sid due to address mismatch ("
99 . $c->{session}{__address} . " != "
100 . $c->request->address . ")",
102 $c->delete_session("address mismatch");
108 my ( $c, $msg ) = @_;
110 # delete the session data
111 my $sid = $c->sessionid;
112 $c->delete_session_data($sid);
114 # reset the values in the context object
115 $c->{session} = undef;
116 $c->sessionid(undef);
117 $c->session_delete_reason($msg);
123 return $c->{session} if $c->{session};
125 my $sid = $c->generate_session_id;
128 $c->log->debug(qq/Created session "$sid"/) if $c->debug;
130 return $c->initialize_session_data;
133 sub initialize_session_data {
138 return $c->{session} = {
141 __expires => $now + $c->config->{session}{expires},
144 $c->config->{session}{verify_address}
145 ? ( __address => $c->request->address )
151 sub generate_session_id {
154 my $digest = $c->_find_digest();
155 $digest->add( $c->session_hash_seed() );
156 return $digest->hexdigest;
161 sub session_hash_seed {
164 return join( "", ++$counter, time, rand, $$, {}, overload::StrVal($c), );
169 sub _find_digest () {
171 foreach my $alg (qw/SHA-1 MD5 SHA-256/) {
173 my $obj = Digest->new($alg);
179 or Catalyst::Exception->throw(
180 "Could not find a suitable Digest module. Please install "
181 . "Digest::SHA1, Digest::SHA, or Digest::MD5" );
184 return Digest->new($usable);
191 $c->NEXT::dump_these(),
194 ? ( [ "Session ID" => $c->sessionid ], [ Session => $c->session ], )
207 Catalyst::Plugin::Session - Generic Session plugin - ties together server side
208 storage and client side tickets required to maintain session data.
212 use Catalyst qw/Session Session::Store::FastMmap Session::State::Cookie/;
214 sub add_item : Local {
215 my ( $self, $c ) = @_;
217 my $item_id = $c->req->param("item");
219 # $c->session is stored across requests, so
220 # other actions will see these values
222 push @{ $c->session->{items} }, $item_id;
224 $c->forward("MyView");
227 sub display_items : Local {
228 my ( $self, $c ) = @_;
230 # values in $c->session are restored
231 $c->stash->{items_to_display} =
232 [ map { MyModel->retrieve($_) } @{ $c->session->{items} } ];
234 $c->forward("MyView");
239 The Session plugin is the base of two related parts of functionality required
240 for session management in web applications.
242 The first part, the State, is getting the browser to repeat back a session key,
243 so that the web application can identify the client and logically string
244 several requests together into a session.
246 The second part, the Store, deals with the actual storage of information about
247 the client. This data is stored so that the it may be revived for every request
248 made by the same client.
250 This plugin links the two pieces together.
258 An accessor for the session ID value.
262 Returns a hash reference that might contain unserialized values from previous
263 requests in the same session, and whose modified value will be saved for future
266 This method will automatically create a new session and session ID if none
269 =item session_delete_reason
271 This accessor contains a string with the reason a session was deleted. Possible
288 This method is extended to also make calls to
289 C<check_session_plugin_requirements> and C<setup_session>.
291 =item check_session_plugin_requirements
293 This method ensures that a State and a Store plugin are also in use by the
298 This method populates C<< $c->config->{session} >> with the default values
299 listed in L</CONFIGURATION>.
303 This methoid is extended, and will restore session data and check it for
304 validity if a session id is defined. It assumes that the State plugin will
305 populate the C<sessionid> key beforehand.
309 This method is extended and will extend the expiry time, as well as persist the
310 session data if a session exists.
312 =item delete_session REASON
314 This method is used to invalidate a session. It takes an optional parameter
315 which will be saved in C<session_delete_reason> if provided.
317 =item initialize_session_data
319 This method will initialize the internal structure of the session, and is
320 called by the C<session> method if appropriate.
322 =item generate_session_id
324 This method will return a string that can be used as a session ID. It is
325 supposed to be a reasonably random string with enough bits to prevent
326 collision. It basically takes C<session_hash_seed> and hashes it using SHA-1,
327 MD5 or SHA-256, depending on the availibility of these modules.
329 =item session_hash_seed
331 This method is actually rather internal to generate_session_id, but should be
332 overridable in case you want to provide more random data.
334 Currently it returns a concatenated string which contains:
348 One value from C<rand>.
352 The stringified value of a newly allocated hash reference
356 The stringified value of the Catalyst context object
360 In the hopes that those combined values are entropic enough for most uses. If
361 this is not the case you can replace C<session_hash_seed> with e.g.
363 sub session_hash_seed {
364 open my $fh, "<", "/dev/random";
365 read $fh, my $bytes, 20;
370 Or even more directly, replace C<generate_session_id>:
372 sub generate_session_id {
373 open my $fh, "<", "/dev/random";
374 read $fh, my $bytes, 20;
376 return unpack("H*", $bytes);
379 Also have a look at L<Crypt::Random> and the various openssl bindings - these
380 modules provide APIs for cryptographically secure random data.
384 See L<Catalyst/dump_these> - ammends the session data structure to the list of
385 dumped objects if session ID is defined.
391 $c->config->{session} = {
395 All configuation parameters are provided in a hash reference under the
396 C<session> key in the configuration hash.
402 The time-to-live of each session, expressed in seconds. Defaults to 7200 (two
407 When false, C<< $c->request->address >> will be checked at prepare time. If it
408 is not the same as the address that initiated the session, the session is
415 The hash reference returned by C<< $c->session >> contains several keys which
416 are automatically set:
422 A timestamp whose value is the last second when the session is still valid. If
423 a session is restored, and __expires is less than the current time, the session
428 The last time a session was saved. This is the value of
429 C<< $c->{session}{__expires} - $c->config->{session}{expires} >>.
433 The time when the session was first created.
437 The value of C<< $c->request->address >> at the time the session was created.
438 This value is only populated of C<verify_address> is true in the configuration.
444 C<verify_address> could make your site inaccessible to users who are behind
445 load balanced proxies. Some ISPs may give a different IP to each request by the
446 same client due to this type of proxying. If addresses are verified these
447 users' sessions cannot persist.
449 To let these users access your site you can either disable address verification
450 as a whole, or provide a checkbox in the login dialog that tells the server
451 that it's OK for the address of the client to change. When the server sees that
452 this box is checked it should delete the C<__address> sepcial key from the
453 session hash when the hash is first created.