Add built local::lib
[catagits/Gitalist.git] / local-lib5 / lib / perl5 / Catalyst / Plugin / Session / Tutorial.pod
CommitLineData
3fea05b9 1=pod
2
3=head1 NAME
4
5Catalyst::Plugin::Session::Tutorial - Understanding and using sessions.
6
7=head1 ASSUMPTIONS
8
9This tutorial assumes that you are familiar with web applications in
10general and Catalyst specifically (up to models and configuration), and
11that you know what HTTP is.
12
13=head1 WHAT ARE SESSIONS
14
15When users use a site, especially one that knows who they are (sites you log in
16to, sites which let you keep a shopping cart, etc.), the server preparing the
17content has to know that request X comes from client A while request Y comes
18from client B, so that each user gets the content meant for them.
19
20The problem is that HTTP is a stateless protocol. This means that every request
21is distinct, and even if it comes from the same client, it's difficult to know
22that.
23
24The way sessions are maintained between distinct requests is that the client
25says, for every request, "I'm client A" or "I'm client B".
26
27This piece of data that tells the server "I'm X" is called the session ID, and
28the threading of several requests together is called a session.
29
30=head1 HOW SESSIONS WORK
31
32=head2 Cookies
33
34HTTP has a feature that lets this become easier, called cookies. A cookie is
35something the server asks the client to save somewhere, and resend every time a
36request is made.
37
38The way they work is that the server sends the C<Set-Cookie> header, with a
39cookie name, a value, and some metadata (like when it expires, what paths it
40applies to, etc.). The client saves this.
41
42Then, on every subsequent request the client will send a C<Cookie> header, with
43the cookie name and value.
44
45=head2 Cookie Alternatives
46
47Another way is to make sure that the session ID is repeated is to include it in
48every URI.
49
50This can be as either a part of the path, or as a query parameter.
51
52This technique has several issues which are discussed in
53L<Catalyst::Plugin::Session::State::URI/CAVEATS>.
54
55=head2 Server-Side Behavior
56
57When the server receives the session ID it can then look this key up in a
58database of some sort. For example the database can contain a shopping cart's
59contents, user preferences, etc.
60
61=head1 USING SESSIONS
62
63In L<Catalyst>, the L<Catalyst::Plugin::Session> plugin provides an API for
64convenient handling of session data. This API is based on the older, less
65flexible and less reliable L<Catalyst::Plugin::Session::FastMmap>.
66
67The plugin is modular, and requires backend plugins to be used.
68
69=head2 State Plugins
70
71State plugins handle session ID persistence. For example
72L<Catalyst::Plugin::Session::State::Cookie> creates a cookie with the session
73ID in it.
74
75These plugins will automatically set C<< $c->sessionid >> at the begining of
76the request, and automatically cause C<< $c->sessionid >> to be saved by the
77client at the end of the request.
78
79=head2 Store Plugins
80
81The backend into which session data is stored is provided by these plugins. For
82example, L<Catalyst::Plugin::Session::Store::DBI> uses a database table to
83store session data, while L<Catalyst::Plugin::Session::Store::FastMmap> uses
84L<Cache::FastMmap>.
85
86=head2 Configuration
87
88First you need to load the appropriate plugins into your L<Catalyst>
89application:
90
91 package MyApp;
92
93 use Catalyst qw/
94 Session
95 Session::State::Cookie
96 Session::Store::File
97 /;
98
99This loads the session API, as well as the required backends of your choice.
100
101After the plugins are loaded they need to be configured. This is done according
102to L<Catalyst::Manual::Cookbook/Configure_your_application>.
103
104Each backend plugin requires its own configuration options (with most plugins
105providing sensible defaults). The session API itself also has configurable
106options listed in L<Catalyst::Plugin::Session/CONFIGURATION>.
107
108For the plugins above we don't need any configuration at all - they should work
109out of the box, but suppose we did want to change some things around, it'll
110look like this:
111
112 MyApp->config( 'Plugin::Session' => {
113 cookie_name => "my_fabulous_cookie",
114 storage => "/path/to/store_data_file",
115 });
116
117=head2 Usage
118
119Now, let's say we have an online shop, and the user is adding an item to the
120shopping cart.
121
122Typically the item the user was viewing would have a form or link that adds the
123item to the cart.
124
125Suppose this link goes to C</cart/add/foo_baz/2>, meaning that we want two
126units of the item C<foo_baz> to be added to the cart.
127
128Our C<add> action should look something like this:
129
130 package MyApp::Controller::Cart;
131
132 sub add : Local {
133 my ( $self, $c, $item_id, $quantity ) = @_;
134 $quantity ||= 1;
135
136 if ( $c->model("Items")->item_exists($item_id) ) {
137 $c->session->{cart}{$item_id} += $quantity;
138 } else {
139 die "No such item";
140 }
141 }
142
143The way this works is that C<< $c->session >> always returns a hash reference
144to some data which is stored by the storage backend plugin. The hash reference
145returned always contains the same items that were in there at the end of the
146last request.
147
148All the mishmash described above is done automatically. First, the method looks
149to see if a session ID is set. This session ID will be set by the State plugin
150if appropriate, at the start of the request (e.g. by looking at the cookies
151sent by the client).
152
153If a session ID is set, the store will be asked to retrieve the session
154data for that specific session ID, and this is returned from
155C<< $c->session >>. This retrieval is cached, and will only happen once per
156request, if at all.
157
158If a session ID is not set, a new one is generated, a new anonymous hash is
159created and saved in the store with the session ID as the key, and the
160reference to the hash is returned.
161
162The action above takes this hash reference, and updates a nested hash within
163it, that counts quantity of each item as stored in the cart.
164
165Any cart-listing code can then look into the session data and use it to display
166the correct items, which will, of course, be remembered across requests.
167
168Here is an action some Template Toolkit example code that could be used to
169generate a cart listing:
170
171 sub list_cart : Local {
172 my ( $self, $c ) = @_;
173
174 # get the cart data, that maps from item_id to quantity
175 my $cart = $c->session->{cart} || {};
176
177 # this is our abstract model in which items are stored
178 my $storage = $c->model("Items");
179
180 # map from item_id to item (an object or hash reference)
181 my %items = map { $_ => $storage->get_item($_) } keys %$cart;
182
183 # put the relevant info on the stash
184 $c->stash->{cart}{items} = \%items;
185 $c->stash->{cart}{quantity} = $cart;
186 }
187
188And [a part of] the template it forwards to:
189
190 <table>
191
192 <thead>
193 <tr>
194 <th>Item</th>
195 <th>Quantity</th>
196 <th>Price</th>
197 <th>remove</th>
198 </tr>
199 </thead>
200
201 <tbody>
202 [%# the table body lists all the items in the cart %]
203 [% FOREACH item_id = cart.items.keys %]
204
205 [%# each item has its own row in the table %]
206
207 [% item = cart.items.$item_id %]
208 [% quantity = cart.quantity.$item_id %]
209
210 <tr>
211 <td>
212 [%# item.name is an attribute in the item
213 # object, as loaded from the store %]
214 [% item.name %]
215 </td>
216
217 <td>
218 [%# supposedly this is part of a form where you
219 # can update the quantity %]
220 <input type="text" name="[% item_id %]_quantity"
221 value="[% quantity %]" />
222 </td>
223
224 <td> $ [% item.price * quantity %] </td>
225
226 <td>
227 <a href="[% c.uri_for('/cart/remove') %]/[% item_id %]">
228 <img src="/static/trash_can.png" />
229 </a>
230 </td>
231 [% END %]
232 <tbody>
233
234 <tfoot>
235 <tr>
236 <td colspan="2"> Total: </td>
237 <td>
238 [%# calculate sum in this cell - too
239 # much headache for a tutorial ;-) %]
240 </td>
241 <td>
242 <a href="[% c.uri_for('/cart/empty') %]">Empty cart</a>
243 </td>
244 </tr>
245 </tfoot>
246
247 </table>
248
249As you can see the way that items are added into C<< $c->session->{cart} >> is
250pretty simple. Since C<< $c->session >> is restored as necessary, and contains
251data from previous requests by the same client, the cart can be updated as the
252user navigates the site pretty transparently.
253
254=head1 SECURITY ISSUES
255
256These issues all relate to how session data is managed, as described above.
257These are not issues you should be concerned about in your application code,
258but are here for their educational value.
259
260=head2 (Not) Trusting the Client
261
262In order to avoid the overhead of server-side data storage, the session data can
263be included in the cookie itself.
264
265There are two problems with this:
266
267=over 4
268
269=item 1
270
271The user can change the data.
272
273=item 2
274
275Cookies have a 4 kilobyte size limit.
276
277The size limit is of no concern in this section, but data changing is. In the
278database scheme the data can be trusted, since the user can neither read nor
279write it. However, if the data is delegated to the user, then special measures
280have to be added for ensuring data integrity, and perhaps secrecy too.
281
282This can be implemented by encrypting and signing the cookie data, but this is
283a big headache.
284
285=back
286
287=head2 Session Hijacking
288
289What happens when client B says "I'm client A"? Well, basically, the server
290buys it. There's no real way around it.
291
292The solution is to make "I'm client A" a difficult thing to say. This is why
293session IDs are randomized. If they are properly randomized, session IDs are so
294hard to guess that they must be stolen instead.
295
296This is called session hijacking. There are several ways one might hijack
297another user's session.
298
299=head3 Cross Site Scripting
300
301One is by using cross site scripting attacks to steal the cookie data. In
302community sites, where users can cause the server to display arbitrary HTML,
303they can use this to put JavaScript code on the server.
304
305If the server does not enforce a strict subset of tags that may be used, the
306malicious user could use this code to steal the cookies (there is a JavaScript
307API that lets cookies be accessed, but this code has to be run on the same
308website that the cookie came from).
309
310=head3 Social Engineering
311
312By tricking a user into revealing a URI with session data embedded in it (when
313cookies are not used), the session ID can also be stolen.
314
315Also, a naive user could be tricked into showing the cookie data from the
316browser to a malicious user.
317
318=head1 AUTHOR
319
320Yuval Kogman E<lt>nothingmuch@woobling.orgE<gt>
321
322=cut