mirror of
https://github.com/actix/actix-web.git
synced 2024-11-20 08:31:09 +00:00
add signed and private cookies
This commit is contained in:
parent
eb66685d1a
commit
9b152acc32
4 changed files with 68 additions and 20 deletions
|
@ -23,6 +23,8 @@
|
||||||
|
|
||||||
* Fix logger request duration calculation #152
|
* Fix logger request duration calculation #152
|
||||||
|
|
||||||
|
* Add `signed` and `private` `CookieSessionBackend`s
|
||||||
|
|
||||||
|
|
||||||
## 0.4.10 (2018-03-20)
|
## 0.4.10 (2018-03-20)
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ fn main() {
|
||||||
.middleware(middleware::Logger::default())
|
.middleware(middleware::Logger::default())
|
||||||
// cookie session middleware
|
// cookie session middleware
|
||||||
.middleware(middleware::SessionStorage::new(
|
.middleware(middleware::SessionStorage::new(
|
||||||
middleware::CookieSessionBackend::new(&[0; 32]).secure(false)
|
middleware::CookieSessionBackend::signed(&[0; 32]).secure(false)
|
||||||
))
|
))
|
||||||
// register favicon
|
// register favicon
|
||||||
.resource("/favicon.ico", |r| r.f(favicon))
|
.resource("/favicon.ico", |r| r.f(favicon))
|
||||||
|
|
|
@ -163,14 +163,17 @@ used with different backend types to store session data in different backends.
|
||||||
> can be added.
|
> can be added.
|
||||||
|
|
||||||
[**CookieSessionBackend**](../actix_web/middleware/struct.CookieSessionBackend.html)
|
[**CookieSessionBackend**](../actix_web/middleware/struct.CookieSessionBackend.html)
|
||||||
uses signed cookies as session storage. `CookieSessionBackend` creates sessions which
|
uses cookies as session storage. `CookieSessionBackend` creates sessions which
|
||||||
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
|
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
|
||||||
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
|
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
|
||||||
|
|
||||||
You need to pass a random value to the constructor of `CookieSessionBackend`.
|
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
|
||||||
This is a private key for cookie session. When this value is changed, all session data is lost.
|
|
||||||
|
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client.
|
||||||
|
|
||||||
|
The constructors take a key as an argument. This is the private key for cookie session - when this value is changed, all session data is lost.
|
||||||
|
|
||||||
|
|
||||||
> **Note**: anything you write into the session is visible by the user, but it is not modifiable.
|
|
||||||
|
|
||||||
In general, you create a
|
In general, you create a
|
||||||
`SessionStorage` middleware and initialize it with specific backend implementation,
|
`SessionStorage` middleware and initialize it with specific backend implementation,
|
||||||
|
@ -203,7 +206,7 @@ fn main() {
|
||||||
server::new(
|
server::new(
|
||||||
|| App::new()
|
|| App::new()
|
||||||
.middleware(SessionStorage::new( // <- create session middleware
|
.middleware(SessionStorage::new( // <- create session middleware
|
||||||
CookieSessionBackend::new(&[0; 32]) // <- create cookie session backend
|
CookieSessionBackend::signed(&[0; 32]) // <- create signed cookie session backend
|
||||||
.secure(false)
|
.secure(false)
|
||||||
)))
|
)))
|
||||||
.bind("127.0.0.1:59880").unwrap()
|
.bind("127.0.0.1:59880").unwrap()
|
||||||
|
|
|
@ -121,7 +121,7 @@ unsafe impl Sync for SessionImplBox {}
|
||||||
/// fn main() {
|
/// fn main() {
|
||||||
/// let app = App::new().middleware(
|
/// let app = App::new().middleware(
|
||||||
/// SessionStorage::new( // <- create session middleware
|
/// SessionStorage::new( // <- create session middleware
|
||||||
/// CookieSessionBackend::new(&[0; 32]) // <- create cookie session backend
|
/// CookieSessionBackend::signed(&[0; 32]) // <- create cookie session backend
|
||||||
/// .secure(false))
|
/// .secure(false))
|
||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
|
@ -257,8 +257,14 @@ impl SessionImpl for CookieSession {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CookieSecurity {
|
||||||
|
Signed,
|
||||||
|
Private
|
||||||
|
}
|
||||||
|
|
||||||
struct CookieSessionInner {
|
struct CookieSessionInner {
|
||||||
key: Key,
|
key: Key,
|
||||||
|
security: CookieSecurity,
|
||||||
name: String,
|
name: String,
|
||||||
path: String,
|
path: String,
|
||||||
domain: Option<String>,
|
domain: Option<String>,
|
||||||
|
@ -268,14 +274,16 @@ struct CookieSessionInner {
|
||||||
|
|
||||||
impl CookieSessionInner {
|
impl CookieSessionInner {
|
||||||
|
|
||||||
fn new(key: &[u8]) -> CookieSessionInner {
|
fn new(key: &[u8], security: CookieSecurity) -> CookieSessionInner {
|
||||||
CookieSessionInner {
|
CookieSessionInner {
|
||||||
key: Key::from_master(key),
|
key: Key::from_master(key),
|
||||||
|
security: security,
|
||||||
name: "actix-session".to_owned(),
|
name: "actix-session".to_owned(),
|
||||||
path: "/".to_owned(),
|
path: "/".to_owned(),
|
||||||
domain: None,
|
domain: None,
|
||||||
secure: true,
|
secure: true,
|
||||||
max_age: None }
|
max_age: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cookie(&self, resp: &mut HttpResponse, state: &HashMap<String, String>) -> Result<()> {
|
fn set_cookie(&self, resp: &mut HttpResponse, state: &HashMap<String, String>) -> Result<()> {
|
||||||
|
@ -299,7 +307,11 @@ impl CookieSessionInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut jar = CookieJar::new();
|
let mut jar = CookieJar::new();
|
||||||
jar.signed(&self.key).add(cookie);
|
|
||||||
|
match self.security {
|
||||||
|
CookieSecurity::Signed => jar.signed(&self.key).add(cookie),
|
||||||
|
CookieSecurity::Private => jar.private(&self.key).add(cookie),
|
||||||
|
}
|
||||||
|
|
||||||
for cookie in jar.delta() {
|
for cookie in jar.delta() {
|
||||||
let val = HeaderValue::from_str(&cookie.to_string())?;
|
let val = HeaderValue::from_str(&cookie.to_string())?;
|
||||||
|
@ -315,7 +327,12 @@ impl CookieSessionInner {
|
||||||
if cookie.name() == self.name {
|
if cookie.name() == self.name {
|
||||||
let mut jar = CookieJar::new();
|
let mut jar = CookieJar::new();
|
||||||
jar.add_original(cookie.clone());
|
jar.add_original(cookie.clone());
|
||||||
if let Some(cookie) = jar.signed(&self.key).get(&self.name) {
|
|
||||||
|
let cookie_opt = match self.security {
|
||||||
|
CookieSecurity::Signed => jar.signed(&self.key).get(&self.name),
|
||||||
|
CookieSecurity::Private => jar.private(&self.key).get(&self.name),
|
||||||
|
};
|
||||||
|
if let Some(cookie) = cookie_opt {
|
||||||
if let Ok(val) = serde_json::from_str(cookie.value()) {
|
if let Ok(val) = serde_json::from_str(cookie.value()) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
@ -327,18 +344,24 @@ impl CookieSessionInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use signed cookies as session storage.
|
/// Use cookies for session storage.
|
||||||
///
|
///
|
||||||
/// `CookieSessionBackend` creates sessions which are limited to storing
|
/// `CookieSessionBackend` creates sessions which are limited to storing
|
||||||
/// fewer than 4000 bytes of data (as the payload must fit into a single cookie).
|
/// fewer than 4000 bytes of data (as the payload must fit into a single cookie).
|
||||||
/// Internal server error get generated if session contains more than 4000 bytes.
|
/// An Internal Server Error is generated if the session contains more than 4000 bytes.
|
||||||
///
|
///
|
||||||
/// You need to pass a random value to the constructor of `CookieSessionBackend`.
|
/// A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
|
||||||
/// This is private key for cookie session, When this value is changed, all session data is lost.
|
|
||||||
///
|
///
|
||||||
/// Note that whatever you write into your session is visible by the user (but not modifiable).
|
/// A *signed* cookie is stored on the client as plaintext alongside
|
||||||
|
/// a signature such that the cookie may be viewed but not modified by the client.
|
||||||
|
///
|
||||||
|
/// A *private* cookie is stored on the client as encrypted text
|
||||||
|
/// such that it may neither be viewed nor modified by the client.
|
||||||
|
///
|
||||||
|
/// The constructors take a key as an argument.
|
||||||
|
/// This is the private key for cookie session - when this value is changed, all session data is lost.
|
||||||
|
/// The constructors will panic if the key is less than 32 bytes in length.
|
||||||
///
|
///
|
||||||
/// Constructor panics if key length is less than 32 bytes.
|
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -347,7 +370,7 @@ impl CookieSessionInner {
|
||||||
/// use actix_web::middleware::CookieSessionBackend;
|
/// use actix_web::middleware::CookieSessionBackend;
|
||||||
///
|
///
|
||||||
/// # fn main() {
|
/// # fn main() {
|
||||||
/// let backend: CookieSessionBackend = CookieSessionBackend::new(&[0; 32])
|
/// let backend: CookieSessionBackend = CookieSessionBackend::signed(&[0; 32])
|
||||||
/// .domain("www.rust-lang.org")
|
/// .domain("www.rust-lang.org")
|
||||||
/// .name("actix_session")
|
/// .name("actix_session")
|
||||||
/// .path("/")
|
/// .path("/")
|
||||||
|
@ -358,12 +381,29 @@ pub struct CookieSessionBackend(Rc<CookieSessionInner>);
|
||||||
|
|
||||||
impl CookieSessionBackend {
|
impl CookieSessionBackend {
|
||||||
|
|
||||||
/// Construct new `CookieSessionBackend` instance.
|
/// Construct new signed `CookieSessionBackend` instance.
|
||||||
///
|
///
|
||||||
/// Panics if key length is less than 32 bytes.
|
/// Panics if key length is less than 32 bytes.
|
||||||
|
#[deprecated(since="0.5.0", note="use `signed` or `private` instead")]
|
||||||
pub fn new(key: &[u8]) -> CookieSessionBackend {
|
pub fn new(key: &[u8]) -> CookieSessionBackend {
|
||||||
CookieSessionBackend(
|
CookieSessionBackend(
|
||||||
Rc::new(CookieSessionInner::new(key)))
|
Rc::new(CookieSessionInner::new(key, CookieSecurity::Signed)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct new *signed* `CookieSessionBackend` instance.
|
||||||
|
///
|
||||||
|
/// Panics if key length is less than 32 bytes.
|
||||||
|
pub fn signed(key: &[u8]) -> CookieSessionBackend {
|
||||||
|
CookieSessionBackend(
|
||||||
|
Rc::new(CookieSessionInner::new(key, CookieSecurity::Signed)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct new *private* `CookieSessionBackend` instance.
|
||||||
|
///
|
||||||
|
/// Panics if key length is less than 32 bytes.
|
||||||
|
pub fn private(key: &[u8]) -> CookieSessionBackend {
|
||||||
|
CookieSessionBackend(
|
||||||
|
Rc::new(CookieSessionInner::new(key, CookieSecurity::Private)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `path` field in the session cookie being built.
|
/// Sets the `path` field in the session cookie being built.
|
||||||
|
@ -385,6 +425,9 @@ impl CookieSessionBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the `secure` field in the session cookie being built.
|
/// Sets the `secure` field in the session cookie being built.
|
||||||
|
///
|
||||||
|
/// If the `secure` field is set, a cookie will only be transmitted when the
|
||||||
|
/// connection is secure - i.e. `https`
|
||||||
pub fn secure(mut self, value: bool) -> CookieSessionBackend {
|
pub fn secure(mut self, value: bool) -> CookieSessionBackend {
|
||||||
Rc::get_mut(&mut self.0).unwrap().secure = value;
|
Rc::get_mut(&mut self.0).unwrap().secure = value;
|
||||||
self
|
self
|
||||||
|
|
Loading…
Reference in a new issue