<!--toc--> |
|
## 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](/wikipp/en/page/cppcms_1x_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` and `set` template member functions use C++ `std::istream` and `std::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 size 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`](http://en.wikipedia.org/wiki/Urandom) 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: |
|
- [Secure Programming: Sessions](/wikipp/en/page/secure_programming#Sessions) |
|
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. |
|
|
|
|