Sessions
General
In this tutoring we would show the basics of using sessions in CppCMS --- how to store connection persistent data in stateless HTTP protocol.
Code
Our current code would be based on start with forms code.
Templates and Content
In this example we would save the information about user using session_interface
object that can be accessed by session()
member function of cppcms::application
.
This information would be displayed to the user every time one visits the page.
First of all let's change our "content.h": Instead of showing person's sex and state we should display a prefix "Mr", "Miss" or "Mrs" according to our saved parameters.
So lets change line:
std::string name,state,sex;
to
std::string name,who;
And we would use following template:
<h1>Hello <%= who %> <%= name %></h1> <% if (content.age != -1.0) %> <p>Your age is <%= age %></p> <h2>Change details</h2> <% else %> <h2>Input your details</h2> <% end %> <form method="post" action="" > <% form as_p info %>
Notes: We can inject arbitrary C++ code into conditions between two brackets ()
(line 2). However, now we should refer to all content class variables using content
member. Templates engine would not substitute correct prefix for you, because it is native C++ code.
Saving data
Now let's rewrite out hello::main
function:
First of all let's load new data from the form when it submitted to session object, instead of content. This information is preserved withing different requests:
content::message c; if(request().request_method()=="POST") { c.info.load(context()); if(c.info.validate()) { session()["name"]=c.info.name.value(); session()["sex"]=c.info.sex.selected_id(); session()["state"]=c.info.martial.selected_id(); session().set("age",c.info.age.value()); c.info.clear(); } }
Notes:
Note on operator
[]
of session object:string &session_interface::operator[](string const &)
It's semantics similar to semantics of
std::map
's[]
operator. Accessing it's member returns the reference to the string by the key. If key not exists, it is created.The storage object is std::string. In order to store other different values, you should do lexical cast to string. The simplest way is to use template member function
template<typename T> void set(string const &key,T const &value);
Now, our session data would be preserved between requests and we can fetch it:
Fetching Data
if(session().is_set("name")) { c.name=session()["name"]; if(session()["sex"]=="m") { c.who="Mr"; } else { if(session()["state"]=="s") { c.who="Miss"; } else { c.who="Mrs"; } } c.age=session().get<double>("age"); } else { c.name="Visitor"; c.age=-1; } render("message",c);
Notes:
First time, you ask for any field, you should check if it is exists:
if(session().is_set("name")) {
It would be incorrect to write:
if(!session()["name"].empty()) {
Because you would change session object and set the value of "name" to empty string, and this value would be saved. This is probably not what you want.
Then, if you always set all fields together, it is safe to fetch them directly. CppCMS sessions guarantees consistency of the data.
You can fetch non-string values using template member function:
template<typename T> T get(std::string const &key)
get
andset
template member functions use C++std::istream
andstd::ostream
to convert the data between the textual representation and its value. So you can use any type T that is writable to and readable from C++ I/O streams.
Configuration --- behind the scenes
Storage Backend
CppCMS has several options to session management. In every case cookies are used --- there is no "GET" or "POST" methods for storing session information like /page/?sid=2e7f60c43b88d4b554a
.
The developer has several options to save information:
- Store all data at client side in signed and optionally encrypted cookies
- Store all data at server side and use session id to retrieve the information each time
- Use combination of them: If amount of data too big to be stored in cookies, it would be stored on server side.
We would add following lines to our CppCMS configuration file:
"session" : { "expire" : "renew", "timeout" : 604800, "location" : "client", "client" : { "hmac" : "sha1", "hmac_key" : "3891bbf7f845fd4277008a63d72640fc13bb9a31" } }
Notes:
- Defining
session.location
, would enable session interface. Most suitable option for 99% of cases is to store the data in signed and encrypted cookies on client side. When using client side storage the signature method (hmac) and a suitable private key should be provided.
Optionally an encryption can be added by setting
session.client.cbc
method like aes and providing a private key.You should keep the private key unique per application and never disclose it. You may generate such key easily using
cppcms_make_key
utility that uses/dev/random
interface.
Duration Options
You can define several session duration options:
- Browser based, until window is closed --- "browser" -- the default one.
- Time based with renewal --- "renew"
- Time based with hard limit after first time creation --- "fixed". It is useful when you want to force user to login every predefined period of time.
Each type of expiration has its time limit (even browser one has its limit). It can be defined using session.timeout
parameter. The default one is 24 hours.
For example:
"expire" : "renew", "timeout" : 604800,
Security Considerations
The full tutorial about sessions and security can be found here:
Make sure you read it and understand it.
Rules of thumb:
- Don't store valuable data in the session, it is ok to store user identification or preferences but it is not good idea to store user's rights.
- If you use client side storage be aware of replay-attacks - where user can restore his cookie to previous state.
- Call
session().reset_session()
after critical operations like authentication in order to prevent session-fixation attacks.
← Working With Forms | Top | Basic Caching →