mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-23 15:51:06 +01:00
chore(actix-session): prepare release 0.9.0
This commit is contained in:
parent
5ceb3c72cd
commit
8b4e8ea34e
@ -25,14 +25,14 @@
|
||||
//! async fn main() -> std::io::Result<()> {
|
||||
//! HttpServer::new(|| {
|
||||
//! let cors = Cors::default()
|
||||
//! .allowed_origin("https://www.rust-lang.org")
|
||||
//! .allowed_origin_fn(|origin, _req_head| {
|
||||
//! origin.as_bytes().ends_with(b".rust-lang.org")
|
||||
//! })
|
||||
//! .allowed_methods(vec!["GET", "POST"])
|
||||
//! .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
|
||||
//! .allowed_header(http::header::CONTENT_TYPE)
|
||||
//! .max_age(3600);
|
||||
//! .allowed_origin("https://www.rust-lang.org")
|
||||
//! .allowed_origin_fn(|origin, _req_head| {
|
||||
//! origin.as_bytes().ends_with(b".rust-lang.org")
|
||||
//! })
|
||||
//! .allowed_methods(vec!["GET", "POST"])
|
||||
//! .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
|
||||
//! .allowed_header(http::header::CONTENT_TYPE)
|
||||
//! .max_age(3600);
|
||||
//!
|
||||
//! App::new()
|
||||
//! .wrap(cors)
|
||||
|
@ -19,7 +19,7 @@ all-features = true
|
||||
|
||||
[dependencies]
|
||||
actix-service = "2"
|
||||
actix-session = "0.8"
|
||||
actix-session = "0.9"
|
||||
actix-utils = "3"
|
||||
actix-web = { version = "4", default-features = false, features = ["cookies", "secure-cookies"] }
|
||||
|
||||
@ -31,7 +31,7 @@ tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
[dev-dependencies]
|
||||
actix-http = "3"
|
||||
actix-web = { version = "4", default-features = false, features = ["macros", "cookies", "secure-cookies"] }
|
||||
actix-session = { version = "0.8", features = ["redis-rs-session", "cookie-session"] }
|
||||
actix-session = { version = "0.9", features = ["redis-rs-session", "cookie-session"] }
|
||||
|
||||
env_logger = "0.10"
|
||||
reqwest = { version = "0.11", default-features = false, features = ["cookies", "json"] }
|
||||
|
@ -11,7 +11,94 @@
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## Documentation & community resources
|
||||
<!-- cargo-rdme start -->
|
||||
|
||||
- [API Documentation](https://docs.rs/actix-identity)
|
||||
- Minimum Supported Rust Version (MSRV): 1.57
|
||||
Identity management for Actix Web.
|
||||
|
||||
`actix-identity` can be used to track identity of a user across multiple requests. It is built on top of HTTP sessions, via [`actix-session`](https://docs.rs/actix-session).
|
||||
|
||||
## Getting started
|
||||
|
||||
To start using identity management in your Actix Web application you must register [`IdentityMiddleware`] and `SessionMiddleware` as middleware on your `App`:
|
||||
|
||||
```rust
|
||||
use actix_web::{cookie::Key, App, HttpServer, HttpResponse};
|
||||
use actix_identity::IdentityMiddleware;
|
||||
use actix_session::{storage::RedisSessionStore, SessionMiddleware};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() {
|
||||
// When using `Key::generate()` it is important to initialize outside of the
|
||||
// `HttpServer::new` closure. When deployed the secret key should be read from a
|
||||
// configuration file or environment variables.
|
||||
let secret_key = Key::generate();
|
||||
|
||||
let redis_store = RedisSessionStore::new("redis://127.0.0.1:6379")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
// Install the identity framework first.
|
||||
.wrap(IdentityMiddleware::default())
|
||||
// The identity system is built on top of sessions. You must install the session
|
||||
// middleware to leverage `actix-identity`. The session middleware must be mounted
|
||||
// AFTER the identity middleware: `actix-web` invokes middleware in the OPPOSITE
|
||||
// order of registration when it receives an incoming request.
|
||||
.wrap(SessionMiddleware::new(
|
||||
redis_store.clone(),
|
||||
secret_key.clone()
|
||||
))
|
||||
// Your request handlers [...]
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
User identities can be created, accessed and destroyed using the [`Identity`] extractor in your request handlers:
|
||||
|
||||
```rust
|
||||
use actix_web::{get, post, HttpResponse, Responder, HttpRequest, HttpMessage};
|
||||
use actix_identity::Identity;
|
||||
use actix_session::storage::RedisSessionStore;
|
||||
|
||||
#[get("/")]
|
||||
async fn index(user: Option<Identity>) -> impl Responder {
|
||||
if let Some(user) = user {
|
||||
format!("Welcome! {}", user.id().unwrap())
|
||||
} else {
|
||||
"Welcome Anonymous!".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/login")]
|
||||
async fn login(request: HttpRequest) -> impl Responder {
|
||||
// Some kind of authentication should happen here
|
||||
// e.g. password-based, biometric, etc.
|
||||
// [...]
|
||||
|
||||
// attach a verified user identity to the active session
|
||||
Identity::login(&request.extensions(), "User1".into()).unwrap();
|
||||
|
||||
HttpResponse::Ok()
|
||||
}
|
||||
|
||||
#[post("/logout")]
|
||||
async fn logout(user: Identity) -> impl Responder {
|
||||
user.logout();
|
||||
HttpResponse::Ok()
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
By default, `actix-identity` does not automatically log out users. You can change this behaviour by customising the configuration for [`IdentityMiddleware`] via [`IdentityMiddleware::builder`].
|
||||
|
||||
In particular, you can automatically log out users who:
|
||||
|
||||
- have been inactive for a while (see [`IdentityMiddlewareBuilder::visit_deadline`];
|
||||
- logged in too long ago (see [`IdentityMiddlewareBuilder::login_deadline`]).
|
||||
|
||||
[`IdentityMiddlewareBuilder::visit_deadline`]: config::IdentityMiddlewareBuilder::visit_deadline
|
||||
[`IdentityMiddlewareBuilder::login_deadline`]: config::IdentityMiddlewareBuilder::login_deadline
|
||||
|
||||
<!-- cargo-rdme end -->
|
||||
|
@ -1,90 +1,96 @@
|
||||
//! Identity management for Actix Web.
|
||||
//!
|
||||
//! `actix-identity` can be used to track identity of a user across multiple requests. It is built
|
||||
//! on top of HTTP sessions, via [`actix-session`](https://docs.rs/actix-session).
|
||||
//!
|
||||
//! # Getting started
|
||||
//! To start using identity management in your Actix Web application you must register
|
||||
//! [`IdentityMiddleware`] and `SessionMiddleware` as middleware on your `App`:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use actix_web::web;
|
||||
//! use actix_web::{cookie::Key, App, HttpServer, HttpResponse};
|
||||
//! use actix_identity::IdentityMiddleware;
|
||||
//! use actix_session::{storage::RedisSessionStore, SessionMiddleware};
|
||||
//!
|
||||
//! #[actix_web::main]
|
||||
//! async fn main() {
|
||||
//! let secret_key = Key::generate();
|
||||
//! let redis_store = RedisSessionStore::new("redis://127.0.0.1:6379")
|
||||
//! .await
|
||||
//! .unwrap();
|
||||
//!
|
||||
//! HttpServer::new(move || {
|
||||
//! App::new()
|
||||
//! // Install the identity framework first.
|
||||
//! .wrap(IdentityMiddleware::default())
|
||||
//! // The identity system is built on top of sessions. You must install the session
|
||||
//! // middleware to leverage `actix-identity`. The session middleware must be mounted
|
||||
//! // AFTER the identity middleware: `actix-web` invokes middleware in the OPPOSITE
|
||||
//! // order of registration when it receives an incoming request.
|
||||
//! .wrap(SessionMiddleware::new(
|
||||
//! redis_store.clone(),
|
||||
//! secret_key.clone()
|
||||
//! ))
|
||||
//! // Your request handlers [...]
|
||||
//! # .default_service(web::to(|| HttpResponse::Ok()))
|
||||
//! })
|
||||
//! # ;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! User identities can be created, accessed and destroyed using the [`Identity`] extractor in your
|
||||
//! request handlers:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use actix_web::{get, post, HttpResponse, Responder, HttpRequest, HttpMessage};
|
||||
//! use actix_identity::Identity;
|
||||
//! use actix_session::storage::RedisSessionStore;
|
||||
//!
|
||||
//! #[get("/")]
|
||||
//! async fn index(user: Option<Identity>) -> impl Responder {
|
||||
//! if let Some(user) = user {
|
||||
//! format!("Welcome! {}", user.id().unwrap())
|
||||
//! } else {
|
||||
//! "Welcome Anonymous!".to_owned()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[post("/login")]
|
||||
//! async fn login(request: HttpRequest) -> impl Responder {
|
||||
//! // Some kind of authentication should happen here
|
||||
//! // e.g. password-based, biometric, etc.
|
||||
//! // [...]
|
||||
//!
|
||||
//! // attach a verified user identity to the active session
|
||||
//! Identity::login(&request.extensions(), "User1".into()).unwrap();
|
||||
//!
|
||||
//! HttpResponse::Ok()
|
||||
//! }
|
||||
//!
|
||||
//! #[post("/logout")]
|
||||
//! async fn logout(user: Identity) -> impl Responder {
|
||||
//! user.logout();
|
||||
//! HttpResponse::Ok()
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Advanced configuration
|
||||
//! By default, `actix-identity` does not automatically log out users. You can change this behaviour
|
||||
//! by customising the configuration for [`IdentityMiddleware`] via [`IdentityMiddleware::builder`].
|
||||
//!
|
||||
//! In particular, you can automatically log out users who:
|
||||
//! - have been inactive for a while (see [`IdentityMiddlewareBuilder::visit_deadline`];
|
||||
//! - logged in too long ago (see [`IdentityMiddlewareBuilder::login_deadline`]).
|
||||
//!
|
||||
//! [`IdentityMiddlewareBuilder::visit_deadline`]: config::IdentityMiddlewareBuilder::visit_deadline
|
||||
//! [`IdentityMiddlewareBuilder::login_deadline`]: config::IdentityMiddlewareBuilder::login_deadline
|
||||
/*!
|
||||
Identity management for Actix Web.
|
||||
|
||||
`actix-identity` can be used to track identity of a user across multiple requests. It is built
|
||||
on top of HTTP sessions, via [`actix-session`](https://docs.rs/actix-session).
|
||||
|
||||
# Getting started
|
||||
To start using identity management in your Actix Web application you must register
|
||||
[`IdentityMiddleware`] and `SessionMiddleware` as middleware on your `App`:
|
||||
|
||||
```no_run
|
||||
# use actix_web::web;
|
||||
use actix_web::{cookie::Key, App, HttpServer, HttpResponse};
|
||||
use actix_identity::IdentityMiddleware;
|
||||
use actix_session::{storage::RedisSessionStore, SessionMiddleware};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() {
|
||||
// When using `Key::generate()` it is important to initialize outside of the
|
||||
// `HttpServer::new` closure. When deployed the secret key should be read from a
|
||||
// configuration file or environment variables.
|
||||
let secret_key = Key::generate();
|
||||
|
||||
let redis_store = RedisSessionStore::new("redis://127.0.0.1:6379")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
// Install the identity framework first.
|
||||
.wrap(IdentityMiddleware::default())
|
||||
// The identity system is built on top of sessions. You must install the session
|
||||
// middleware to leverage `actix-identity`. The session middleware must be mounted
|
||||
// AFTER the identity middleware: `actix-web` invokes middleware in the OPPOSITE
|
||||
// order of registration when it receives an incoming request.
|
||||
.wrap(SessionMiddleware::new(
|
||||
redis_store.clone(),
|
||||
secret_key.clone(),
|
||||
))
|
||||
// Your request handlers [...]
|
||||
# .default_service(web::to(|| HttpResponse::Ok()))
|
||||
})
|
||||
# ;
|
||||
}
|
||||
```
|
||||
|
||||
User identities can be created, accessed and destroyed using the [`Identity`] extractor in your
|
||||
request handlers:
|
||||
|
||||
```no_run
|
||||
use actix_web::{get, post, HttpResponse, Responder, HttpRequest, HttpMessage};
|
||||
use actix_identity::Identity;
|
||||
use actix_session::storage::RedisSessionStore;
|
||||
|
||||
#[get("/")]
|
||||
async fn index(user: Option<Identity>) -> impl Responder {
|
||||
if let Some(user) = user {
|
||||
format!("Welcome! {}", user.id().unwrap())
|
||||
} else {
|
||||
"Welcome Anonymous!".to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/login")]
|
||||
async fn login(request: HttpRequest) -> impl Responder {
|
||||
// Some kind of authentication should happen here
|
||||
// e.g. password-based, biometric, etc.
|
||||
// [...]
|
||||
|
||||
// attach a verified user identity to the active session
|
||||
Identity::login(&request.extensions(), "User1".into()).unwrap();
|
||||
|
||||
HttpResponse::Ok()
|
||||
}
|
||||
|
||||
#[post("/logout")]
|
||||
async fn logout(user: Identity) -> impl Responder {
|
||||
user.logout();
|
||||
HttpResponse::Ok()
|
||||
}
|
||||
```
|
||||
|
||||
# Advanced configuration
|
||||
By default, `actix-identity` does not automatically log out users. You can change this behaviour
|
||||
by customising the configuration for [`IdentityMiddleware`] via [`IdentityMiddleware::builder`].
|
||||
|
||||
In particular, you can automatically log out users who:
|
||||
- have been inactive for a while (see [`IdentityMiddlewareBuilder::visit_deadline`];
|
||||
- logged in too long ago (see [`IdentityMiddlewareBuilder::login_deadline`]).
|
||||
|
||||
[`IdentityMiddlewareBuilder::visit_deadline`]: config::IdentityMiddlewareBuilder::visit_deadline
|
||||
[`IdentityMiddlewareBuilder::login_deadline`]: config::IdentityMiddlewareBuilder::login_deadline
|
||||
*/
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style, missing_docs)]
|
||||
|
@ -32,7 +32,7 @@ redis = { version = "0.24", default-features = false, features = ["tokio-comp"]
|
||||
time = "0.3"
|
||||
|
||||
# session
|
||||
actix-session = { version = "0.8", optional = true }
|
||||
actix-session = { version = "0.9", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-web = "4"
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.9.0
|
||||
|
||||
- Remove use of `async-trait` on `SessionStore` trait.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "actix-session"
|
||||
version = "0.8.0"
|
||||
version = "0.9.0"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Luca Palmieri <rust@lpalmieri.com>",
|
||||
|
@ -5,14 +5,130 @@
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
[![crates.io](https://img.shields.io/crates/v/actix-session?label=latest)](https://crates.io/crates/actix-session)
|
||||
[![Documentation](https://docs.rs/actix-session/badge.svg?version=0.8.0)](https://docs.rs/actix-session/0.8.0)
|
||||
[![Documentation](https://docs.rs/actix-session/badge.svg?version=0.9.0)](https://docs.rs/actix-session/0.9.0)
|
||||
![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-session)
|
||||
[![Dependency Status](https://deps.rs/crate/actix-session/0.8.0/status.svg)](https://deps.rs/crate/actix-session/0.8.0)
|
||||
[![Dependency Status](https://deps.rs/crate/actix-session/0.9.0/status.svg)](https://deps.rs/crate/actix-session/0.9.0)
|
||||
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## Documentation & Resources
|
||||
<!-- cargo-rdme start -->
|
||||
|
||||
- [API Documentation](https://docs.rs/actix-session)
|
||||
- [Example Projects](https://github.com/actix/examples/tree/master/auth/cookie-session)
|
||||
- Minimum Supported Rust Version (MSRV): 1.57
|
||||
Session management for Actix Web.
|
||||
|
||||
The HTTP protocol, at a first glance, is stateless: the client sends a request, the server parses its content, performs some processing and returns a response. The outcome is only influenced by the provided inputs (i.e. the request content) and whatever state the server queries while performing its processing.
|
||||
|
||||
Stateless systems are easier to reason about, but they are not quite as powerful as we need them to be - e.g. how do you authenticate a user? The user would be forced to authenticate **for every single request**. That is, for example, how 'Basic' Authentication works. While it may work for a machine user (i.e. an API client), it is impractical for a person—you do not want a login prompt on every single page you navigate to!
|
||||
|
||||
There is a solution - **sessions**. Using sessions the server can attach state to a set of requests coming from the same client. They are built on top of cookies - the server sets a cookie in the HTTP response (`Set-Cookie` header), the client (e.g. the browser) will store the cookie and play it back to the server when sending new requests (using the `Cookie` header).
|
||||
|
||||
We refer to the cookie used for sessions as a **session cookie**. Its content is called **session key** (or **session ID**), while the state attached to the session is referred to as **session state**.
|
||||
|
||||
`actix-session` provides an easy-to-use framework to manage sessions in applications built on top of Actix Web. [`SessionMiddleware`] is the middleware underpinning the functionality provided by `actix-session`; it takes care of all the session cookie handling and instructs the **storage backend** to create/delete/update the session state based on the operations performed against the active [`Session`].
|
||||
|
||||
`actix-session` provides some built-in storage backends: ([`CookieSessionStore`], [`RedisSessionStore`], and [`RedisActorSessionStore`]) - you can create a custom storage backend by implementing the [`SessionStore`] trait.
|
||||
|
||||
Further reading on sessions:
|
||||
|
||||
- [RFC 6265](https://datatracker.ietf.org/doc/html/rfc6265);
|
||||
- [OWASP's session management cheat-sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html).
|
||||
|
||||
## Getting started
|
||||
|
||||
To start using sessions in your Actix Web application you must register [`SessionMiddleware`] as a middleware on your `App`:
|
||||
|
||||
```rust
|
||||
use actix_web::{web, App, HttpServer, HttpResponse, Error};
|
||||
use actix_session::{Session, SessionMiddleware, storage::RedisActorSessionStore};
|
||||
use actix_web::cookie::Key;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
// When using `Key::generate()` it is important to initialize outside of the
|
||||
// `HttpServer::new` closure. When deployed the secret key should be read from a
|
||||
// configuration file or environment variables.
|
||||
let secret_key = Key::generate();
|
||||
|
||||
let redis_store = RedisSessionStore::new("redis://127.0.0.1:6379")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
HttpServer::new(move ||
|
||||
App::new()
|
||||
// Add session management to your application using Redis for session state storage
|
||||
.wrap(
|
||||
SessionMiddleware::new(
|
||||
redis_store.clone(),
|
||||
secret_key.clone(),
|
||||
)
|
||||
)
|
||||
.default_service(web::to(|| HttpResponse::Ok())))
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
```
|
||||
|
||||
The session state can be accessed and modified by your request handlers using the [`Session`] extractor. Note that this doesn't work in the stream of a streaming response.
|
||||
|
||||
```rust
|
||||
use actix_web::Error;
|
||||
use actix_session::Session;
|
||||
|
||||
fn index(session: Session) -> Result<&'static str, Error> {
|
||||
// access the session state
|
||||
if let Some(count) = session.get::<i32>("counter")? {
|
||||
println!("SESSION value: {}", count);
|
||||
// modify the session state
|
||||
session.insert("counter", count + 1)?;
|
||||
} else {
|
||||
session.insert("counter", 1)?;
|
||||
}
|
||||
|
||||
Ok("Welcome!")
|
||||
}
|
||||
```
|
||||
|
||||
## Choosing A Backend
|
||||
|
||||
By default, `actix-session` does not provide any storage backend to retrieve and save the state attached to your sessions. You can enable:
|
||||
|
||||
- a purely cookie-based "backend", [`CookieSessionStore`], using the `cookie-session` feature flag.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["cookie-session"] }
|
||||
```
|
||||
|
||||
- a Redis-based backend via [`actix-redis`](https://docs.rs/actix-redis), [`RedisActorSessionStore`], using the `redis-actor-session` feature flag.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["redis-actor-session"] }
|
||||
```
|
||||
|
||||
- a Redis-based backend via [`redis-rs`](https://docs.rs/redis-rs), [`RedisSessionStore`], using the `redis-rs-session` feature flag.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["redis-rs-session"] }
|
||||
```
|
||||
|
||||
Add the `redis-rs-tls-session` feature flag if you want to connect to Redis using a secured connection:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["redis-rs-session", "redis-rs-tls-session"] }
|
||||
```
|
||||
|
||||
You can implement your own session storage backend using the [`SessionStore`] trait.
|
||||
|
||||
[`SessionStore`]: storage::SessionStore
|
||||
[`CookieSessionStore`]: storage::CookieSessionStore
|
||||
[`RedisSessionStore`]: storage::RedisSessionStore
|
||||
[`RedisActorSessionStore`]: storage::RedisActorSessionStore
|
||||
|
||||
<!-- cargo-rdme end -->
|
||||
|
@ -1,137 +1,145 @@
|
||||
//! Session management for Actix Web.
|
||||
//!
|
||||
//! The HTTP protocol, at a first glance, is stateless: the client sends a request, the server
|
||||
//! parses its content, performs some processing and returns a response. The outcome is only
|
||||
//! influenced by the provided inputs (i.e. the request content) and whatever state the server
|
||||
//! queries while performing its processing.
|
||||
//!
|
||||
//! Stateless systems are easier to reason about, but they are not quite as powerful as we need them
|
||||
//! to be - e.g. how do you authenticate a user? The user would be forced to authenticate **for
|
||||
//! every single request**. That is, for example, how 'Basic' Authentication works. While it may
|
||||
//! work for a machine user (i.e. an API client), it is impractical for a person—you do not want a
|
||||
//! login prompt on every single page you navigate to!
|
||||
//!
|
||||
//! There is a solution - **sessions**. Using sessions the server can attach state to a set of
|
||||
//! requests coming from the same client. They are built on top of cookies - the server sets a
|
||||
//! cookie in the HTTP response (`Set-Cookie` header), the client (e.g. the browser) will store the
|
||||
//! cookie and play it back to the server when sending new requests (using the `Cookie` header).
|
||||
//!
|
||||
//! We refer to the cookie used for sessions as a **session cookie**. Its content is called
|
||||
//! **session key** (or **session ID**), while the state attached to the session is referred to as
|
||||
//! **session state**.
|
||||
//!
|
||||
//! `actix-session` provides an easy-to-use framework to manage sessions in applications built on
|
||||
//! top of Actix Web. [`SessionMiddleware`] is the middleware underpinning the functionality
|
||||
//! provided by `actix-session`; it takes care of all the session cookie handling and instructs the
|
||||
//! **storage backend** to create/delete/update the session state based on the operations performed
|
||||
//! against the active [`Session`].
|
||||
//!
|
||||
//! `actix-session` provides some built-in storage backends: ([`CookieSessionStore`],
|
||||
//! [`RedisSessionStore`], and [`RedisActorSessionStore`]) - you can create a custom storage backend
|
||||
//! by implementing the [`SessionStore`] trait.
|
||||
//!
|
||||
//! Further reading on sessions:
|
||||
//! - [RFC6265](https://datatracker.ietf.org/doc/html/rfc6265);
|
||||
//! - [OWASP's session management cheat-sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html).
|
||||
//!
|
||||
//! # Getting started
|
||||
//! To start using sessions in your Actix Web application you must register [`SessionMiddleware`]
|
||||
//! as a middleware on your `App`:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use actix_web::{web, App, HttpServer, HttpResponse, Error};
|
||||
//! use actix_session::{Session, SessionMiddleware, storage::RedisActorSessionStore};
|
||||
//! use actix_web::cookie::Key;
|
||||
//!
|
||||
//! #[actix_web::main]
|
||||
//! async fn main() -> std::io::Result<()> {
|
||||
//! // The secret key would usually be read from a configuration file/environment variables.
|
||||
//! let secret_key = Key::generate();
|
||||
//! let redis_connection_string = "127.0.0.1:6379";
|
||||
//! HttpServer::new(move ||
|
||||
//! App::new()
|
||||
//! // Add session management to your application using Redis for session state storage
|
||||
//! .wrap(
|
||||
//! SessionMiddleware::new(
|
||||
//! RedisActorSessionStore::new(redis_connection_string),
|
||||
//! secret_key.clone()
|
||||
//! )
|
||||
//! )
|
||||
//! .default_service(web::to(|| HttpResponse::Ok())))
|
||||
//! .bind(("127.0.0.1", 8080))?
|
||||
//! .run()
|
||||
//! .await
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The session state can be accessed and modified by your request handlers using the [`Session`]
|
||||
//! extractor. Note that this doesn't work in the stream of a streaming response.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use actix_web::Error;
|
||||
//! use actix_session::Session;
|
||||
//!
|
||||
//! fn index(session: Session) -> Result<&'static str, Error> {
|
||||
//! // access the session state
|
||||
//! if let Some(count) = session.get::<i32>("counter")? {
|
||||
//! println!("SESSION value: {}", count);
|
||||
//! // modify the session state
|
||||
//! session.insert("counter", count + 1)?;
|
||||
//! } else {
|
||||
//! session.insert("counter", 1)?;
|
||||
//! }
|
||||
//!
|
||||
//! Ok("Welcome!")
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Choosing A Backend
|
||||
//!
|
||||
//! By default, `actix-session` does not provide any storage backend to retrieve and save the state
|
||||
//! attached to your sessions. You can enable:
|
||||
//!
|
||||
//! - a purely cookie-based "backend", [`CookieSessionStore`], using the `cookie-session` feature
|
||||
//! flag.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! # ...
|
||||
//! actix-session = { version = "...", features = ["cookie-session"] }
|
||||
//! ```
|
||||
//!
|
||||
//! - a Redis-based backend via [`actix-redis`](https://docs.rs/actix-redis),
|
||||
//! [`RedisActorSessionStore`], using the `redis-actor-session` feature flag.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! # ...
|
||||
//! actix-session = { version = "...", features = ["redis-actor-session"] }
|
||||
//! ```
|
||||
//!
|
||||
//! - a Redis-based backend via [`redis-rs`](https://docs.rs/redis-rs), [`RedisSessionStore`], using
|
||||
//! the `redis-rs-session` feature flag.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! # ...
|
||||
//! actix-session = { version = "...", features = ["redis-rs-session"] }
|
||||
//! ```
|
||||
//!
|
||||
//! Add the `redis-rs-tls-session` feature flag if you want to connect to Redis using a secured
|
||||
//! connection:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! # ...
|
||||
//! actix-session = { version = "...", features = ["redis-rs-session", "redis-rs-tls-session"] }
|
||||
//! ```
|
||||
//!
|
||||
//! You can implement your own session storage backend using the [`SessionStore`] trait.
|
||||
//!
|
||||
//! [`SessionStore`]: storage::SessionStore
|
||||
//! [`CookieSessionStore`]: storage::CookieSessionStore
|
||||
//! [`RedisSessionStore`]: storage::RedisSessionStore
|
||||
//! [`RedisActorSessionStore`]: storage::RedisActorSessionStore
|
||||
/*!
|
||||
Session management for Actix Web.
|
||||
|
||||
The HTTP protocol, at a first glance, is stateless: the client sends a request, the server
|
||||
parses its content, performs some processing and returns a response. The outcome is only
|
||||
influenced by the provided inputs (i.e. the request content) and whatever state the server
|
||||
queries while performing its processing.
|
||||
|
||||
Stateless systems are easier to reason about, but they are not quite as powerful as we need them
|
||||
to be - e.g. how do you authenticate a user? The user would be forced to authenticate **for
|
||||
every single request**. That is, for example, how 'Basic' Authentication works. While it may
|
||||
work for a machine user (i.e. an API client), it is impractical for a person—you do not want a
|
||||
login prompt on every single page you navigate to!
|
||||
|
||||
There is a solution - **sessions**. Using sessions the server can attach state to a set of
|
||||
requests coming from the same client. They are built on top of cookies - the server sets a
|
||||
cookie in the HTTP response (`Set-Cookie` header), the client (e.g. the browser) will store the
|
||||
cookie and play it back to the server when sending new requests (using the `Cookie` header).
|
||||
|
||||
We refer to the cookie used for sessions as a **session cookie**. Its content is called
|
||||
**session key** (or **session ID**), while the state attached to the session is referred to as
|
||||
**session state**.
|
||||
|
||||
`actix-session` provides an easy-to-use framework to manage sessions in applications built on
|
||||
top of Actix Web. [`SessionMiddleware`] is the middleware underpinning the functionality
|
||||
provided by `actix-session`; it takes care of all the session cookie handling and instructs the
|
||||
**storage backend** to create/delete/update the session state based on the operations performed
|
||||
against the active [`Session`].
|
||||
|
||||
`actix-session` provides some built-in storage backends: ([`CookieSessionStore`],
|
||||
[`RedisSessionStore`], and [`RedisActorSessionStore`]) - you can create a custom storage backend
|
||||
by implementing the [`SessionStore`] trait.
|
||||
|
||||
Further reading on sessions:
|
||||
- [RFC 6265](https://datatracker.ietf.org/doc/html/rfc6265);
|
||||
- [OWASP's session management cheat-sheet](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html).
|
||||
|
||||
# Getting started
|
||||
To start using sessions in your Actix Web application you must register [`SessionMiddleware`]
|
||||
as a middleware on your `App`:
|
||||
|
||||
```no_run
|
||||
use actix_web::{web, App, HttpServer, HttpResponse, Error};
|
||||
use actix_session::{Session, SessionMiddleware, storage::RedisSessionStore};
|
||||
use actix_web::cookie::Key;
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
// When using `Key::generate()` it is important to initialize outside of the
|
||||
// `HttpServer::new` closure. When deployed the secret key should be read from a
|
||||
// configuration file or environment variables.
|
||||
let secret_key = Key::generate();
|
||||
|
||||
let redis_store = RedisSessionStore::new("redis://127.0.0.1:6379")
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
HttpServer::new(move ||
|
||||
App::new()
|
||||
// Add session management to your application using Redis for session state storage
|
||||
.wrap(
|
||||
SessionMiddleware::new(
|
||||
redis_store.clone(),
|
||||
secret_key.clone(),
|
||||
)
|
||||
)
|
||||
.default_service(web::to(|| HttpResponse::Ok())))
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
```
|
||||
|
||||
The session state can be accessed and modified by your request handlers using the [`Session`]
|
||||
extractor. Note that this doesn't work in the stream of a streaming response.
|
||||
|
||||
```no_run
|
||||
use actix_web::Error;
|
||||
use actix_session::Session;
|
||||
|
||||
fn index(session: Session) -> Result<&'static str, Error> {
|
||||
// access the session state
|
||||
if let Some(count) = session.get::<i32>("counter")? {
|
||||
println!("SESSION value: {}", count);
|
||||
// modify the session state
|
||||
session.insert("counter", count + 1)?;
|
||||
} else {
|
||||
session.insert("counter", 1)?;
|
||||
}
|
||||
|
||||
Ok("Welcome!")
|
||||
}
|
||||
```
|
||||
|
||||
# Choosing A Backend
|
||||
|
||||
By default, `actix-session` does not provide any storage backend to retrieve and save the state
|
||||
attached to your sessions. You can enable:
|
||||
|
||||
- a purely cookie-based "backend", [`CookieSessionStore`], using the `cookie-session` feature
|
||||
flag.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["cookie-session"] }
|
||||
```
|
||||
|
||||
- a Redis-based backend via [`actix-redis`](https://docs.rs/actix-redis),
|
||||
[`RedisActorSessionStore`], using the `redis-actor-session` feature flag.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["redis-actor-session"] }
|
||||
```
|
||||
|
||||
- a Redis-based backend via [`redis-rs`](https://docs.rs/redis-rs), [`RedisSessionStore`], using
|
||||
the `redis-rs-session` feature flag.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["redis-rs-session"] }
|
||||
```
|
||||
|
||||
Add the `redis-rs-tls-session` feature flag if you want to connect to Redis using a secured
|
||||
connection:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
# ...
|
||||
actix-session = { version = "...", features = ["redis-rs-session", "redis-rs-tls-session"] }
|
||||
```
|
||||
|
||||
You can implement your own session storage backend using the [`SessionStore`] trait.
|
||||
|
||||
[`SessionStore`]: storage::SessionStore
|
||||
[`CookieSessionStore`]: storage::CookieSessionStore
|
||||
[`RedisSessionStore`]: storage::RedisSessionStore
|
||||
[`RedisActorSessionStore`]: storage::RedisActorSessionStore
|
||||
*/
|
||||
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
|
7
justfile
7
justfile
@ -2,13 +2,16 @@ _list:
|
||||
@just --list
|
||||
|
||||
# Format workspace.
|
||||
fmt:
|
||||
fmt: update-readmes
|
||||
cargo +nightly fmt
|
||||
npx -y prettier --write $(fd --hidden --extension=yml --extension=md)
|
||||
|
||||
# Update READMEs from crate root documentation.
|
||||
update-readmes: && fmt
|
||||
update-readmes:
|
||||
cd ./actix-cors && cargo rdme --force
|
||||
cd ./actix-session && cargo rdme --force
|
||||
cd ./actix-identity && cargo rdme --force
|
||||
npx -y prettier --write $(fd README.md)
|
||||
|
||||
# Document crates in workspace.
|
||||
doc:
|
||||
|
Loading…
Reference in New Issue
Block a user