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