From 844e44805a86b94c7261ad9b3989016004016026 Mon Sep 17 00:00:00 2001 From: owen Date: Mon, 7 Jul 2025 23:15:41 +0800 Subject: [PATCH 01/20] add session --- src/configuration/app_config.rs | 10 ++++++++ src/layer/mod.rs | 1 + src/layer/session_layer.rs | 1 + src/session/mod.rs | 2 ++ src/session/session.rs | 22 +++++++++++++++++ src/session/sesson_manage.rs | 44 +++++++++++++++++++++++++++++++++ 6 files changed, 80 insertions(+) create mode 100644 src/layer/session_layer.rs create mode 100644 src/session/mod.rs create mode 100644 src/session/session.rs create mode 100644 src/session/sesson_manage.rs diff --git a/src/configuration/app_config.rs b/src/configuration/app_config.rs index 7f31530..873c15c 100644 --- a/src/configuration/app_config.rs +++ b/src/configuration/app_config.rs @@ -8,6 +8,7 @@ pub struct AppConfig { database: DatabaseSettings, server: ServerSettings, file_location: FileSettings, + session: SessionSettings, } #[derive(Debug, Clone, Deserialize)] @@ -32,6 +33,11 @@ struct FileSettings { blog_home_dir: String, } +#[derive(Debug, Clone, Deserialize)] +struct SessionSettings { + timeout: u16, +} + // 使用 OnceLock 实现配置的单例缓存 static CONFIG: OnceLock = OnceLock::new(); @@ -95,4 +101,8 @@ impl AppConfig { self.file_location.blog_home_dir.clone() + self.file_location.archive_location.clone().as_str() } + + pub fn get_session_timeout(&self) -> u16 { + self.session.timeout + } } diff --git a/src/layer/mod.rs b/src/layer/mod.rs index f9f79cd..4286ccd 100644 --- a/src/layer/mod.rs +++ b/src/layer/mod.rs @@ -1,2 +1,3 @@ pub mod log_layer; +pub mod session_layer; pub mod url_layer; diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/layer/session_layer.rs @@ -0,0 +1 @@ + diff --git a/src/session/mod.rs b/src/session/mod.rs new file mode 100644 index 0000000..4de486c --- /dev/null +++ b/src/session/mod.rs @@ -0,0 +1,2 @@ +pub mod session; +pub mod session_manage; diff --git a/src/session/session.rs b/src/session/session.rs new file mode 100644 index 0000000..c86f066 --- /dev/null +++ b/src/session/session.rs @@ -0,0 +1,22 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +struct session { + id: String, + username: String, + email: String, + role: String, + created_at: chrono::DateTime, +} + +impl session { + pub fn new(id: String, username: String, email: String, role: String) -> Self { + session { + id, + username, + email, + role, + created_at: chrono::Utc::now(), + } + } +} diff --git a/src/session/sesson_manage.rs b/src/session/sesson_manage.rs new file mode 100644 index 0000000..8085f16 --- /dev/null +++ b/src/session/sesson_manage.rs @@ -0,0 +1,44 @@ +use std::collections::HashMap; +use std::sync::Arc; + +struct SessionManager { + timeout: u16, + sessions: Arc>>, //hashmap线程不安全, Arc 允许多线程同时访问, 但需要使用RwLock来保证线程安全 +} + +const DEFAULT_TIMEOUT: u16 = 1800; //默认时间为1800秒 + +impl SessionManager { + fn new(timeout: Option) -> Self { + let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT as u64); + SessionManager { + timeout: timeout as u16, + sessions: Arc::new(RwLock::new(HashMap::with_capacity(1))), + } + } + + fn add_session(&mut self, id: String, session: Session) { + self.sessions.write().unwrap().insert(id, session); + } + + fn remove_session(&mut self, id: &str) { + self.sessions.write().unwrap().remove(id); + } + + fn get_session(&self, id: &str) -> Option<&Session> { + self.sessions.read().unwrap().get(id) + } + + fn is_session_exist(&self, id: &str) -> bool { + self.sessions.read().unwrap().contains_key(id) + } + + // 如果不存在则插入,返回session + // 如果存在则返回已存在的session + fn insert_if_not_exist(&mut self, id: String, session: Session) -> &Self { + if !self.is_session_exist(&id) { + self.add_session(id, session); + } + &self + } +} -- Gitee From 795db6397ace26e23efb4a587c615fc927080753 Mon Sep 17 00:00:00 2001 From: owen Date: Mon, 7 Jul 2025 23:52:17 +0800 Subject: [PATCH 02/20] login html --- src/main.rs | 44 ++++++++++++--------------------------- templates/html/login.html | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 31 deletions(-) create mode 100644 templates/html/login.html diff --git a/src/main.rs b/src/main.rs index e2958a0..9b73354 100644 --- a/src/main.rs +++ b/src/main.rs @@ -79,6 +79,8 @@ async fn main() { .route("/articles/:page_num", get(article_list)) //返回具体信息 .route("/article/:id", get(article_detail)) .route("/me", get(me)) + .route("/login", get(to_login)) + .route("/loign", post(login)) .route("/article/new", get(extract_article)) .layer( //设置同源策略 @@ -188,37 +190,7 @@ async fn upload_page() -> Html { let upload_page_html = fs::read_to_string("./templates/html/blog_upload.html").unwrap(); Html(upload_page_html) } -/* -async fn upload_file1(mut multipart: Multipart) -> &'static str { - let mut response = "上传成功"; - while let Some(field) = multipart.next_field().await.map_err(|e| { - error!("upload file failed {}", e); - return "上传文件失败!"; - })? { - let name = field.file_name().unwrap().to_string(); - let data = field.bytes().await.unwrap(); - println!("Length of `{}` is {} bytes", name, data.len()); - //保存文件 - //let file_path = std::env::current_dir().unwrap().join("markdown").join(name); - let file_path_string = AppConfig::get().get_file_upload_location() + "/" + &name; - - let file_path = std::path::Path::new(&file_path_string); - info!(&file_path_string); - //检查文件是否存在 - if file_path.exists() { - //存在则更新文件,同时更新数据库信息 - fs::write(file_path, data).expect("更新文件失败!"); - //todo db update - } else { - fs::write(file_path, data).expect("保存文件失败!"); - } - //上传单文件,所以这里直接返回成功 - return response; - } - response = "没有接受到上传的文件,请检查是否上传文件!"; - return response; -} -*/ + async fn upload_file(mut multipart: Multipart) -> &'static str { // 循环读取每一个字段 while let Some(field) = match multipart.next_field().await { @@ -270,6 +242,16 @@ async fn upload_file(mut multipart: Multipart) -> &'static str { "没有接收到上传的文件,请检查是否选择文件!" } +async fn to_login() -> Html { + let login_page = fs::read_to_string("./templates/html/login.html").unwrap(); + Html(login_page) +} + +async fn login() -> Html { + //validate username and password, todo + upload_page().await +} + #[cfg(test)] mod test { use super::*; diff --git a/templates/html/login.html b/templates/html/login.html new file mode 100644 index 0000000..90e42ab --- /dev/null +++ b/templates/html/login.html @@ -0,0 +1,41 @@ + + + + + + + + + RustU + + +
+
+
+
Master,快登陆吧!
+
+
+ +
+
+ +
+
+ 上舰 +
+
+ +
+
+
+ + -- Gitee From 7f93479447fb0369cc44e73a518ef3ed6d31ffb7 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 8 Jul 2025 00:09:32 +0800 Subject: [PATCH 03/20] login page --- templates/html/login.html | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/templates/html/login.html b/templates/html/login.html index 90e42ab..3312aff 100644 --- a/templates/html/login.html +++ b/templates/html/login.html @@ -12,20 +12,47 @@ type="text/javascript" src="/assets/js/bootstrap.bundle.min.js" > + RustU
-
+
Master,快登陆吧!
-
- +
+
-
- +
+
上舰 -- Gitee From 3946edb505f82580f73733e5f530f1e1d8bb9dfc Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 17 Jul 2025 00:32:23 +0800 Subject: [PATCH 04/20] session --- resource/application.toml | 9 ++- src/configuration/app_config.rs | 4 +- src/layer/session_layer.rs | 65 +++++++++++++++++++ src/layer/url_layer.rs | 1 + src/lib.rs | 1 + src/main.rs | 4 ++ src/session/session.rs | 10 +-- .../{sesson_manage.rs => session_manage.rs} | 28 ++++---- 8 files changed, 93 insertions(+), 29 deletions(-) rename src/session/{sesson_manage.rs => session_manage.rs} (57%) diff --git a/resource/application.toml b/resource/application.toml index 758304b..d9ccce3 100644 --- a/resource/application.toml +++ b/resource/application.toml @@ -3,13 +3,12 @@ host = "0.0.0.0" port = 3000 [database] -host = "xxxx" -port = xxxx -username = "xxx" -password = "xxxx" -database = "xxxx" + [file_location] blog_home_dir = "/home/owen/blog/" upload_location = "markdown" archive_location = "archive" + +[session] +timeout = 1800 diff --git a/src/configuration/app_config.rs b/src/configuration/app_config.rs index 873c15c..91d5725 100644 --- a/src/configuration/app_config.rs +++ b/src/configuration/app_config.rs @@ -102,7 +102,7 @@ impl AppConfig { + self.file_location.archive_location.clone().as_str() } - pub fn get_session_timeout(&self) -> u16 { - self.session.timeout + pub fn get_session_timeout(&self) -> Option { + Some(self.session.timeout) } } diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 8b13789..58be949 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -1 +1,66 @@ +use std::{ + fmt, + task::{Context, Poll}, +}; +use axum::body::Body; +use axum::http::Request; + +use crate::session::session_manage::SessionManager; + +/// a demo https://tower-rs.github.io/tower/tower_layer/trait.Layer.html +use tower_layer::Layer; +use tower_service::Service; + +#[derive(Clone)] +pub struct SessionLayer { + session_manager: SessionManager, +} + +impl SessionLayer { + pub fn new(timeout: Option) -> Self { + SessionLayer { + session_manager: SessionManager::new(timeout), + } + } +} + +impl Layer for SessionLayer { + type Service = SessionService; + + fn layer(&self, service: S) -> Self::Service { + SessionService { + service, + } + } +} + +// This service implements the Log behavior +#[derive(Debug, Clone)] +pub struct SessionService { + service: S, +} + +impl Service> for SessionService +where + S: Service>, + S::Future: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + fn call(&mut self, mut req: Request) -> Self::Future { + if let Some(value) = req.headers().get("sessionid") { + if let Ok(session_id) = value.to_str() { + println!("Found session ID: {}", session_id); + } + } + //todo check if session exists + self.service.call(req) + } +} diff --git a/src/layer/url_layer.rs b/src/layer/url_layer.rs index ffcd398..1d81a44 100644 --- a/src/layer/url_layer.rs +++ b/src/layer/url_layer.rs @@ -11,6 +11,7 @@ use tklog::warn; use tower_layer::Layer; use tower_service::Service; + #[derive(Debug, Clone)] pub struct UrlLayer { articls_click_vue: Arc>>, diff --git a/src/lib.rs b/src/lib.rs index 7532655..e9d650d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,4 @@ pub mod entity; pub mod job; pub mod layer; pub mod scheduler_job; +pub mod session; diff --git a/src/main.rs b/src/main.rs index 9b73354..96a597b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ use axum_hello::entity::article_list_view::ArticlesListView; use axum_hello::layer::log_layer::LogLayer; use axum_hello::layer::url_layer::UrlLayer; use axum_hello::scheduler_job::article_read_time_scheduler_job::ArticleReadTimeSchedulerJob; +use axum_hello::layer::session_layer::SessionLayer; use sqlx::mysql::MySqlPoolOptions; use sqlx::MySqlPool; @@ -71,6 +72,8 @@ async fn main() { let log_layer = LogLayer { target: "hello layer", }; + let timeout = AppConfig::get().get_session_timeout(); + let session_layer = SessionLayer::new(timeout); let router = Router::new() .route("/blog/new", post(upload_file)) .route("/blog/uploadpage", get(upload_page)) @@ -88,6 +91,7 @@ async fn main() { ) .layer(log_layer) .layer(url_layer) + .layer(session_layer) .with_state(pool.clone()) .nest_service("/assets", tower_http::services::ServeDir::new("assets")); diff --git a/src/session/session.rs b/src/session/session.rs index c86f066..e477ffb 100644 --- a/src/session/session.rs +++ b/src/session/session.rs @@ -1,22 +1,22 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] -struct session { +pub struct Session { id: String, username: String, email: String, role: String, - created_at: chrono::DateTime, + //created_at: chrono::DateTime, } -impl session { +impl Session { pub fn new(id: String, username: String, email: String, role: String) -> Self { - session { + Session { id, username, email, role, - created_at: chrono::Utc::now(), + // created_at: chrono::Utc::now(), } } } diff --git a/src/session/sesson_manage.rs b/src/session/session_manage.rs similarity index 57% rename from src/session/sesson_manage.rs rename to src/session/session_manage.rs index 8085f16..e88d0fa 100644 --- a/src/session/sesson_manage.rs +++ b/src/session/session_manage.rs @@ -1,44 +1,38 @@ use std::collections::HashMap; use std::sync::Arc; +use std::sync::RwLock; -struct SessionManager { +use crate::session::session::Session; + +#[derive(Clone)] +pub struct SessionManager { timeout: u16, sessions: Arc>>, //hashmap线程不安全, Arc 允许多线程同时访问, 但需要使用RwLock来保证线程安全 } const DEFAULT_TIMEOUT: u16 = 1800; //默认时间为1800秒 +const DEFAULT_SESSION_COUNT: usize = 1; impl SessionManager { - fn new(timeout: Option) -> Self { - let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT as u64); + pub fn new(timeout: Option) -> Self { + let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); SessionManager { - timeout: timeout as u16, - sessions: Arc::new(RwLock::new(HashMap::with_capacity(1))), + timeout, + sessions: Arc::new(RwLock::new(HashMap::with_capacity(DEFAULT_SESSION_COUNT))), //只有管理员需要登陆 } } fn add_session(&mut self, id: String, session: Session) { self.sessions.write().unwrap().insert(id, session); + } fn remove_session(&mut self, id: &str) { self.sessions.write().unwrap().remove(id); } - fn get_session(&self, id: &str) -> Option<&Session> { - self.sessions.read().unwrap().get(id) - } fn is_session_exist(&self, id: &str) -> bool { self.sessions.read().unwrap().contains_key(id) } - - // 如果不存在则插入,返回session - // 如果存在则返回已存在的session - fn insert_if_not_exist(&mut self, id: String, session: Session) -> &Self { - if !self.is_session_exist(&id) { - self.add_session(id, session); - } - &self - } } -- Gitee From 388b2a5d49040e0dff92e7b09558d2aa01205d48 Mon Sep 17 00:00:00 2001 From: owen Date: Fri, 18 Jul 2025 01:03:28 +0800 Subject: [PATCH 05/20] logic of check session exist and timeout --- src/configuration/app_config.rs | 4 +-- src/layer/session_layer.rs | 50 ++++++++++++++++++++++++++++----- src/session/session.rs | 10 +++++-- src/session/session_manage.rs | 18 +++++++----- 4 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/configuration/app_config.rs b/src/configuration/app_config.rs index 91d5725..f4841a5 100644 --- a/src/configuration/app_config.rs +++ b/src/configuration/app_config.rs @@ -35,7 +35,7 @@ struct FileSettings { #[derive(Debug, Clone, Deserialize)] struct SessionSettings { - timeout: u16, + timeout: u64, } // 使用 OnceLock 实现配置的单例缓存 @@ -102,7 +102,7 @@ impl AppConfig { + self.file_location.archive_location.clone().as_str() } - pub fn get_session_timeout(&self) -> Option { + pub fn get_session_timeout(&self) -> Option { Some(self.session.timeout) } } diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 58be949..c5cedfb 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -5,6 +5,9 @@ use std::{ use axum::body::Body; use axum::http::Request; +use axum::response::Redirect; +use axum::http::StatusCode; +use axum::response::IntoResponse; use crate::session::session_manage::SessionManager; @@ -18,7 +21,7 @@ pub struct SessionLayer { } impl SessionLayer { - pub fn new(timeout: Option) -> Self { + pub fn new(timeout: Option) -> Self { SessionLayer { session_manager: SessionManager::new(timeout), } @@ -31,6 +34,7 @@ impl Layer for SessionLayer { fn layer(&self, service: S) -> Self::Service { SessionService { service, + session_manager: self.session_manager.clone(), } } } @@ -39,6 +43,7 @@ impl Layer for SessionLayer { #[derive(Debug, Clone)] pub struct SessionService { service: S, + session_manager: SessionManager, } impl Service> for SessionService @@ -54,13 +59,44 @@ where self.service.poll_ready(cx) } - fn call(&mut self, mut req: Request) -> Self::Future { - if let Some(value) = req.headers().get("sessionid") { - if let Ok(session_id) = value.to_str() { - println!("Found session ID: {}", session_id); - } + fn call(&mut self, req: Request) -> Self::Future { + + let uri = req.uri().path(); + println!("Request URI: {}", uri); + if uri== "/blog/uploadpage" { + let response = match req.headers().get("sessionid") { + Some(value) => { + if let Ok(session_id) = value.to_str() { + println!("Found session ID: {}", session_id); + if self.session_manager.is_session_exist(&session_id) { + if self.session_manager.is_expired(&session_id) { + let response = Redirect::temporary("/login"); + Some(response) + } else { + None + } + }else { + let response = Redirect::temporary("/login"); + Some(response) + } + } else { + eprintln!("Invalid session ID"); + None + } + } + None => { + println!("No session ID found"); + //need redirect to login + let response = Redirect::temporary("/login"); + Some(response) + } + }; + + if let Some(response) = response { + println!("Returning response"); + todo!("return response"); + }; } - //todo check if session exists self.service.call(req) } } diff --git a/src/session/session.rs b/src/session/session.rs index e477ffb..6510709 100644 --- a/src/session/session.rs +++ b/src/session/session.rs @@ -1,12 +1,12 @@ use serde::{Deserialize, Serialize}; - +use std::time::SystemTime; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Session { id: String, username: String, email: String, role: String, - //created_at: chrono::DateTime, + time: SystemTime, } impl Session { @@ -16,7 +16,11 @@ impl Session { username, email, role, - // created_at: chrono::Utc::now(), + time: SystemTime::now(), } } + + pub fn is_expired(&self, duration: u64) -> bool { + self.time.elapsed().unwrap().as_secs() >= duration + } } diff --git a/src/session/session_manage.rs b/src/session/session_manage.rs index e88d0fa..46eff15 100644 --- a/src/session/session_manage.rs +++ b/src/session/session_manage.rs @@ -4,17 +4,17 @@ use std::sync::RwLock; use crate::session::session::Session; -#[derive(Clone)] +#[derive(Clone,Debug)] pub struct SessionManager { - timeout: u16, + timeout: u64, sessions: Arc>>, //hashmap线程不安全, Arc 允许多线程同时访问, 但需要使用RwLock来保证线程安全 } -const DEFAULT_TIMEOUT: u16 = 1800; //默认时间为1800秒 +const DEFAULT_TIMEOUT: u64 = 1800; //默认时间为1800秒 const DEFAULT_SESSION_COUNT: usize = 1; impl SessionManager { - pub fn new(timeout: Option) -> Self { + pub fn new(timeout: Option) -> Self { let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); SessionManager { timeout, @@ -22,17 +22,21 @@ impl SessionManager { } } - fn add_session(&mut self, id: String, session: Session) { + pub fn add_session(&mut self, id: String, session: Session) { self.sessions.write().unwrap().insert(id, session); } - fn remove_session(&mut self, id: &str) { + pub fn remove_session(&mut self, id: &str) { self.sessions.write().unwrap().remove(id); } - fn is_session_exist(&self, id: &str) -> bool { + pub fn is_session_exist(&self, id: &str) -> bool { self.sessions.read().unwrap().contains_key(id) } + + pub fn is_expired(&self, id: &str) -> bool { + self.sessions.read().unwrap().get(id).map_or(false, |session| session.is_expired(self.timeout)) + } } -- Gitee From 82f7620646b1315a5c4ca2bdfeb40a84973f89f4 Mon Sep 17 00:00:00 2001 From: owen Date: Fri, 18 Jul 2025 01:05:05 +0800 Subject: [PATCH 06/20] cargo fmt to format code --- src/layer/session_layer.rs | 7 +++---- src/layer/url_layer.rs | 1 - src/main.rs | 2 +- src/session/session_manage.rs | 12 +++++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index c5cedfb..36b3cd5 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -5,9 +5,9 @@ use std::{ use axum::body::Body; use axum::http::Request; -use axum::response::Redirect; use axum::http::StatusCode; use axum::response::IntoResponse; +use axum::response::Redirect; use crate::session::session_manage::SessionManager; @@ -60,10 +60,9 @@ where } fn call(&mut self, req: Request) -> Self::Future { - let uri = req.uri().path(); println!("Request URI: {}", uri); - if uri== "/blog/uploadpage" { + if uri == "/blog/uploadpage" { let response = match req.headers().get("sessionid") { Some(value) => { if let Ok(session_id) = value.to_str() { @@ -75,7 +74,7 @@ where } else { None } - }else { + } else { let response = Redirect::temporary("/login"); Some(response) } diff --git a/src/layer/url_layer.rs b/src/layer/url_layer.rs index 1d81a44..ffcd398 100644 --- a/src/layer/url_layer.rs +++ b/src/layer/url_layer.rs @@ -11,7 +11,6 @@ use tklog::warn; use tower_layer::Layer; use tower_service::Service; - #[derive(Debug, Clone)] pub struct UrlLayer { articls_click_vue: Arc>>, diff --git a/src/main.rs b/src/main.rs index 96a597b..2cd93b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,9 +13,9 @@ use axum::{ }; use axum_hello::entity::article_list_view::ArticlesListView; use axum_hello::layer::log_layer::LogLayer; +use axum_hello::layer::session_layer::SessionLayer; use axum_hello::layer::url_layer::UrlLayer; use axum_hello::scheduler_job::article_read_time_scheduler_job::ArticleReadTimeSchedulerJob; -use axum_hello::layer::session_layer::SessionLayer; use sqlx::mysql::MySqlPoolOptions; use sqlx::MySqlPool; diff --git a/src/session/session_manage.rs b/src/session/session_manage.rs index 46eff15..283a4b8 100644 --- a/src/session/session_manage.rs +++ b/src/session/session_manage.rs @@ -4,7 +4,7 @@ use std::sync::RwLock; use crate::session::session::Session; -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct SessionManager { timeout: u64, sessions: Arc>>, //hashmap线程不安全, Arc 允许多线程同时访问, 但需要使用RwLock来保证线程安全 @@ -14,7 +14,7 @@ const DEFAULT_TIMEOUT: u64 = 1800; //默认时间为1800秒 const DEFAULT_SESSION_COUNT: usize = 1; impl SessionManager { - pub fn new(timeout: Option) -> Self { + pub fn new(timeout: Option) -> Self { let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); SessionManager { timeout, @@ -24,19 +24,21 @@ impl SessionManager { pub fn add_session(&mut self, id: String, session: Session) { self.sessions.write().unwrap().insert(id, session); - } pub fn remove_session(&mut self, id: &str) { self.sessions.write().unwrap().remove(id); } - pub fn is_session_exist(&self, id: &str) -> bool { self.sessions.read().unwrap().contains_key(id) } pub fn is_expired(&self, id: &str) -> bool { - self.sessions.read().unwrap().get(id).map_or(false, |session| session.is_expired(self.timeout)) + self.sessions + .read() + .unwrap() + .get(id) + .map_or(false, |session| session.is_expired(self.timeout)) } } -- Gitee From fe963cedf3c645bb2fbca78eac542d9668a1d5bd Mon Sep 17 00:00:00 2001 From: owen Date: Sat, 19 Jul 2025 15:32:34 +0800 Subject: [PATCH 07/20] session --- src/layer/session_layer.rs | 48 ++++++++++++++++---------------------- src/main.rs | 4 ++-- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 36b3cd5..87c585c 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -1,12 +1,11 @@ use std::{ - fmt, task::{Context, Poll}, + future::Future, + pin::Pin, }; - -use axum::body::Body; -use axum::http::Request; -use axum::http::StatusCode; use axum::response::IntoResponse; +use axum::body::Body; +use axum::http::{Request,Response}; use axum::response::Redirect; use crate::session::session_manage::SessionManager; @@ -34,7 +33,7 @@ impl Layer for SessionLayer { fn layer(&self, service: S) -> Self::Service { SessionService { service, - session_manager: self.session_manager.clone(), + session_manager: self.session_manager.clone() } } } @@ -48,12 +47,13 @@ pub struct SessionService { impl Service> for SessionService where - S: Service>, - S::Future: Send + 'static, + S: Service, Response = Response>, + S::Future: 'static, + S::Response: Send+'static, { - type Response = S::Response; + type Response = Response; type Error = S::Error; - type Future = S::Future; + type Future= Pin>>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) @@ -62,40 +62,32 @@ where fn call(&mut self, req: Request) -> Self::Future { let uri = req.uri().path(); println!("Request URI: {}", uri); - if uri == "/blog/uploadpage" { - let response = match req.headers().get("sessionid") { + if uri == "uploadpage" { + let should_redirect = match req.headers().get("sessionid") { Some(value) => { if let Ok(session_id) = value.to_str() { println!("Found session ID: {}", session_id); if self.session_manager.is_session_exist(&session_id) { - if self.session_manager.is_expired(&session_id) { - let response = Redirect::temporary("/login"); - Some(response) - } else { - None - } + self.session_manager.is_expired(&session_id) } else { - let response = Redirect::temporary("/login"); - Some(response) + true } } else { - eprintln!("Invalid session ID"); - None + true } } None => { println!("No session ID found"); - //need redirect to login - let response = Redirect::temporary("/login"); - Some(response) + true } }; - if let Some(response) = response { + if should_redirect { println!("Returning response"); - todo!("return response"); + let response = Redirect::temporary("/login"); + return Box::pin(async {Ok(response.into_response())}); }; } - self.service.call(req) + Box::pin(self.service.call(req)) } } diff --git a/src/main.rs b/src/main.rs index 2cd93b7..b4768e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,7 +76,7 @@ async fn main() { let session_layer = SessionLayer::new(timeout); let router = Router::new() .route("/blog/new", post(upload_file)) - .route("/blog/uploadpage", get(upload_page)) + .route("/uploadpage", get(upload_page)) .route("/", get(articles_view)) //只返回试图,路由作用 .route("/articles", get(articles_view)) //只返回试图,路由作用 .route("/articles/:page_num", get(article_list)) //返回具体信息 @@ -87,7 +87,7 @@ async fn main() { .route("/article/new", get(extract_article)) .layer( //设置同源策略 - CorsLayer::new().allow_origin("*".parse::().unwrap()), + CorsLayer::new().allow_origin("*".parse::().unwrap()),//need to fix ) .layer(log_layer) .layer(url_layer) -- Gitee From f7f0f0af475d2306082c4367a3ed7d08ecd12350 Mon Sep 17 00:00:00 2001 From: owen Date: Mon, 21 Jul 2025 23:41:24 +0800 Subject: [PATCH 08/20] session --- src/layer/session_layer.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 87c585c..a5ad13a 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -48,12 +48,12 @@ pub struct SessionService { impl Service> for SessionService where S: Service, Response = Response>, - S::Future: 'static, + S::Future: Send+'static, S::Response: Send+'static, { type Response = Response; type Error = S::Error; - type Future= Pin>>>; + type Future= Pin>+Send+'static>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) @@ -62,16 +62,12 @@ where fn call(&mut self, req: Request) -> Self::Future { let uri = req.uri().path(); println!("Request URI: {}", uri); - if uri == "uploadpage" { + if uri == "/uploadpage" { let should_redirect = match req.headers().get("sessionid") { Some(value) => { if let Ok(session_id) = value.to_str() { println!("Found session ID: {}", session_id); - if self.session_manager.is_session_exist(&session_id) { - self.session_manager.is_expired(&session_id) - } else { - true - } + !self.session_manager.is_session_exist(&session_id) || self.session_manager.is_expired(&session_id) } else { true } -- Gitee From 10230d9d188c9e8225c5db2b869e84ce9474f960 Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 23 Jul 2025 02:09:54 +0800 Subject: [PATCH 09/20] session and login --- Cargo.toml | 2 +- resource/application.toml | 10 +++- src/configuration/app_config.rs | 11 +++++ src/entity/login_form.rs | 7 +++ src/entity/mod.rs | 1 + src/layer/session_layer.rs | 4 +- src/main.rs | 40 +++++++++++++--- templates/html/login.html | 83 ++++++++++++++++++++++++++------- 8 files changed, 130 insertions(+), 28 deletions(-) create mode 100644 src/entity/login_form.rs diff --git a/Cargo.toml b/Cargo.toml index 7ac36cf..ce4280e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axum={version = "0.7.5", features=["multipart"]} +axum={version = "0.7.5", features=["multipart","form"]} tokio={version="1.37.0",features=["macros","rt-multi-thread","time"]} askama = {version="0.12.1"} askama_axum="0.4.0" diff --git a/resource/application.toml b/resource/application.toml index d9ccce3..58bf5ef 100644 --- a/resource/application.toml +++ b/resource/application.toml @@ -3,7 +3,11 @@ host = "0.0.0.0" port = 3000 [database] - +host = "xx.xx.xx.xx" +port = xxx +username = "xxx" +password = "xx" +database = "Blog" [file_location] blog_home_dir = "/home/owen/blog/" @@ -12,3 +16,7 @@ archive_location = "archive" [session] timeout = 1800 + +[admin] +username = "admin" +password = "admin" diff --git a/src/configuration/app_config.rs b/src/configuration/app_config.rs index f4841a5..221200d 100644 --- a/src/configuration/app_config.rs +++ b/src/configuration/app_config.rs @@ -9,6 +9,7 @@ pub struct AppConfig { server: ServerSettings, file_location: FileSettings, session: SessionSettings, + admin: AdminSettings, } #[derive(Debug, Clone, Deserialize)] @@ -38,6 +39,12 @@ struct SessionSettings { timeout: u64, } +#[derive(Debug, Clone, Deserialize)] +pub struct AdminSettings { + pub username: String, + pub password: String, +} + // 使用 OnceLock 实现配置的单例缓存 static CONFIG: OnceLock = OnceLock::new(); @@ -105,4 +112,8 @@ impl AppConfig { pub fn get_session_timeout(&self) -> Option { Some(self.session.timeout) } + + pub fn get_admin_settings(&self) -> &AdminSettings { + &self.admin + } } diff --git a/src/entity/login_form.rs b/src/entity/login_form.rs new file mode 100644 index 0000000..645e507 --- /dev/null +++ b/src/entity/login_form.rs @@ -0,0 +1,7 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct LoginForm { + pub username: String, + pub password: String, +} diff --git a/src/entity/mod.rs b/src/entity/mod.rs index f38666a..95017a8 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -4,3 +4,4 @@ pub mod article_detail; pub mod article_list_view; pub mod article_summary; pub mod article_summary_vec; +pub mod login_form; diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index a5ad13a..d94431a 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -62,7 +62,7 @@ where fn call(&mut self, req: Request) -> Self::Future { let uri = req.uri().path(); println!("Request URI: {}", uri); - if uri == "/uploadpage" { + if uri == "/toupload1" { let should_redirect = match req.headers().get("sessionid") { Some(value) => { if let Ok(session_id) = value.to_str() { @@ -80,7 +80,7 @@ where if should_redirect { println!("Returning response"); - let response = Redirect::temporary("/login"); + let response = Redirect::temporary("/tologin"); return Box::pin(async {Ok(response.into_response())}); }; } diff --git a/src/main.rs b/src/main.rs index b4768e2..e8ccf74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,24 +3,34 @@ use std::sync::atomic::AtomicU16; use std::sync::{Arc, RwLock}; use axum::extract::Multipart; -use axum::http::HeaderValue; +use axum::Form; use axum::response::Html; +use axum::response::IntoResponse; +use axum::http::{Request,Response}; +use axum::body::Body; + + use axum::{ extract::Path, extract::State, routing::{get, post}, + http::{StatusCode,Uri}, Json, Router, }; +use axum::response::Redirect; use axum_hello::entity::article_list_view::ArticlesListView; use axum_hello::layer::log_layer::LogLayer; use axum_hello::layer::session_layer::SessionLayer; use axum_hello::layer::url_layer::UrlLayer; use axum_hello::scheduler_job::article_read_time_scheduler_job::ArticleReadTimeSchedulerJob; +use axum::http::HeaderValue; use sqlx::mysql::MySqlPoolOptions; use sqlx::MySqlPool; use tower_http::cors::CorsLayer; +use job_scheduler::Uuid; + use tklog::{error, info, warn}; pub mod configuration; @@ -34,6 +44,7 @@ use crate::entity::article_detail::ArticleDetail; use crate::entity::article_summary::ArticleSummary; use crate::entity::article_summary_vec::ArticleSummaryVec; use crate::person::Person; +use crate::entity::login_form::LoginForm; use crate::job::file_extractor::Extractor; @@ -76,15 +87,16 @@ async fn main() { let session_layer = SessionLayer::new(timeout); let router = Router::new() .route("/blog/new", post(upload_file)) - .route("/uploadpage", get(upload_page)) + .route("/toupload", get(upload_page)) .route("/", get(articles_view)) //只返回试图,路由作用 .route("/articles", get(articles_view)) //只返回试图,路由作用 .route("/articles/:page_num", get(article_list)) //返回具体信息 .route("/article/:id", get(article_detail)) .route("/me", get(me)) - .route("/login", get(to_login)) - .route("/loign", post(login)) + .route("/tologin", get(to_login)) + .route("/login", post(login)) .route("/article/new", get(extract_article)) + .fallback(status_sode_404) .layer( //设置同源策略 CorsLayer::new().allow_origin("*".parse::().unwrap()),//need to fix @@ -93,6 +105,7 @@ async fn main() { .layer(url_layer) .layer(session_layer) .with_state(pool.clone()) + .with_state(app_config.clone()) .nest_service("/assets", tower_http::services::ServeDir::new("assets")); //开启scheudler @@ -115,6 +128,10 @@ async fn say_hello(State(pool): State) -> String { row.0 } +async fn status_sode_404(uri: Uri) -> (StatusCode, String) { + (StatusCode::NOT_FOUND, format!("No route for {uri}")) +} + async fn articles_view() -> ArticlesListView { ArticlesListView::new() } @@ -251,9 +268,18 @@ async fn to_login() -> Html { Html(login_page) } -async fn login() -> Html { - //validate username and password, todo - upload_page().await +async fn login(Form(form):Form) -> Result, StatusCode> { + println!("login"); + let app_config = AppConfig::get(); + println!("app_config: {:?}", app_config); + let admin = app_config.get_admin_settings(); + if admin.username == form.username && admin.password == form.password { + let mut response = Redirect::to("/toupload").into_response(); + //let session_id = Uuid::new_v4().to_string(); + response.headers_mut().insert("Set-Cookie", HeaderValue::from_str(&format!("sessionid=world")).unwrap()); + return Ok(response); + } + return Err(StatusCode::UNAUTHORIZED); } #[cfg(test)] diff --git a/templates/html/login.html b/templates/html/login.html index 3312aff..9f773f0 100644 --- a/templates/html/login.html +++ b/templates/html/login.html @@ -32,6 +32,7 @@ } + RustU @@ -40,23 +41,41 @@
Master,快登陆吧!
-
- -
-
- -
-
- 上舰 -
+
+
+ +
+
+ +
+
+ + + +
+
+ -- Gitee From 300f4baa65231873b9fac2d2dd4348e1d2a088b6 Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 23 Jul 2025 02:12:32 +0800 Subject: [PATCH 10/20] style --- templates/html/login.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/templates/html/login.html b/templates/html/login.html index 9f773f0..eef6567 100644 --- a/templates/html/login.html +++ b/templates/html/login.html @@ -73,7 +73,9 @@ --> - +
-- Gitee From bd377f7e6038de36709bba5583ee00b7eb1e131d Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 23 Jul 2025 02:17:15 +0800 Subject: [PATCH 11/20] remove unuseful code --- templates/html/login.html | 40 --------------------------------------- 1 file changed, 40 deletions(-) diff --git a/templates/html/login.html b/templates/html/login.html index eef6567..1924a5b 100644 --- a/templates/html/login.html +++ b/templates/html/login.html @@ -63,16 +63,6 @@ />
- - @@ -85,35 +75,5 @@
- -- Gitee From b156733cdc8a45d2db1d5ff7e4d04da8cd751f98 Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 23 Jul 2025 22:36:46 +0800 Subject: [PATCH 12/20] cookie expire data --- src/main.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index e8ccf74..ca62990 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,6 +33,8 @@ use job_scheduler::Uuid; use tklog::{error, info, warn}; +use chrono::{DateTime, Duration, Utc}; + pub mod configuration; pub mod entity; pub mod job; @@ -274,9 +276,13 @@ async fn login(Form(form):Form) -> Result, StatusCode> println!("app_config: {:?}", app_config); let admin = app_config.get_admin_settings(); if admin.username == form.username && admin.password == form.password { - let mut response = Redirect::to("/toupload").into_response(); - //let session_id = Uuid::new_v4().to_string(); - response.headers_mut().insert("Set-Cookie", HeaderValue::from_str(&format!("sessionid=world")).unwrap()); + let mut response = Redirect::to("/").into_response();//redirect to home page + let now: DateTime = Utc::now(); + let thirty_minutes_later = now + Duration::minutes(30); + let expire_time = thirty_minutes_later.format("%a, %d %b %Y %T GMT"); + let session_id = Uuid::new_v4().to_string(); + let cookies = format!("sessionid={};Expires={}",session_id, expire_time); + response.headers_mut().insert("Set-Cookie", HeaderValue::from_str(cookies.as_str()).unwrap()); return Ok(response); } return Err(StatusCode::UNAUTHORIZED); -- Gitee From 090d4e98a3e7f7b30f070402e0dfa5142f5c5b2a Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 23 Jul 2025 23:16:12 +0800 Subject: [PATCH 13/20] session layer extract session and check session --- src/layer/session_layer.rs | 33 ++++++++++++++++++++++----------- templates/html/blogs.html | 11 +++++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index d94431a..25b176c 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -62,22 +62,33 @@ where fn call(&mut self, req: Request) -> Self::Future { let uri = req.uri().path(); println!("Request URI: {}", uri); - if uri == "/toupload1" { - let should_redirect = match req.headers().get("sessionid") { - Some(value) => { - if let Ok(session_id) = value.to_str() { - println!("Found session ID: {}", session_id); - !self.session_manager.is_session_exist(&session_id) || self.session_manager.is_expired(&session_id) - } else { + if uri == "/toupload" { + let cookies = req.headers().get("cookie").unwrap(); + println!("the cookies:{:?}", cookies.to_str()); + let cookies = cookies.to_str().unwrap(); + let should_redirect = if cookies.contains("sessionid") { + let session_id = cookies.split(';') + .map(|s| s.trim()) + .find(|s| s.starts_with("sessionid=")) + .and_then(|s| s.split_once('=')) + .map(|(_, v)| v).unwrap(); + println!("the sessionid:{:?}", session_id); + + if self.session_manager.is_session_exist(&session_id){ + if self.session_manager.is_expired(&session_id){ + self.session_manager.remove_session(&session_id); true + }else{ + false } - } - None => { - println!("No session ID found"); + }else{ true } - }; + + }else{ + true + }; if should_redirect { println!("Returning response"); let response = Redirect::temporary("/tologin"); diff --git a/templates/html/blogs.html b/templates/html/blogs.html index 5fd7aa7..ba5df43 100644 --- a/templates/html/blogs.html +++ b/templates/html/blogs.html @@ -31,6 +31,17 @@ Click me! + + Upload Markdown! + +
Date: Wed, 23 Jul 2025 23:20:58 +0800 Subject: [PATCH 14/20] =?UTF-8?q?readme=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2a282b2..71ed741 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,13 @@ 定时任务:job_scheduler 模板渲染:aksama, askama-axum 页面:html,css,js, VUE, Bootstrap +配置: config ### 服务器端口 -端口 3000, 硬编码在代码中,需要改进 +端口 3000, 写入到配置文件中application.toml ### 数据库 -数据库信息硬编码在数据库中,需要改进,用配置文件或者.env。 +写入到配置文件中 ### 技术细节 -- Gitee From c36582f119310405238e7fd3e142175ff401bf20 Mon Sep 17 00:00:00 2001 From: owen Date: Wed, 23 Jul 2025 23:23:06 +0800 Subject: [PATCH 15/20] =?UTF-8?q?readme=20=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 71ed741..7fe5f34 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,12 @@ 日志:tklog 定时任务:job_scheduler 模板渲染:aksama, askama-axum -页面:html,css,js, VUE, Bootstrap +页面:html,css,js, VUE, Bootstrap, marked.umd.js 配置: config +### 解析Markdown +在前段解析Markdown文档,使用的lib为marked.umd.js + ### 服务器端口 端口 3000, 写入到配置文件中application.toml -- Gitee From fd926a79ec10bdc7eaf3155773c06b81677b5b9e Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 24 Jul 2025 13:17:19 +0800 Subject: [PATCH 16/20] =?UTF-8?q?OnceLock=20=E5=92=8Csession?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layer/session_layer.rs | 61 ++++++++++++++++++----------------- src/lib.rs | 3 -- src/main.rs | 47 +++++++++++++++------------ src/session/session_manage.rs | 29 +++++++++++++++-- 4 files changed, 84 insertions(+), 56 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 25b176c..e3c48d5 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -1,12 +1,12 @@ +use axum::body::Body; +use axum::http::{Request, Response}; +use axum::response::IntoResponse; +use axum::response::Redirect; use std::{ - task::{Context, Poll}, future::Future, pin::Pin, + task::{Context, Poll}, }; -use axum::response::IntoResponse; -use axum::body::Body; -use axum::http::{Request,Response}; -use axum::response::Redirect; use crate::session::session_manage::SessionManager; @@ -15,25 +15,25 @@ use tower_layer::Layer; use tower_service::Service; #[derive(Clone)] -pub struct SessionLayer { - session_manager: SessionManager, +pub struct SessionLayer<'a> { + session_manager: &'a SessionManager, } -impl SessionLayer { +impl SessionLayer<'_> { pub fn new(timeout: Option) -> Self { - SessionLayer { - session_manager: SessionManager::new(timeout), - } + SessionManager::init(timeout).expect("Initialized SessionManager failed!"); + let session_manager = SessionManager::get(); + SessionLayer { session_manager } } } -impl Layer for SessionLayer { +impl Layer for SessionLayer<'_> { type Service = SessionService; fn layer(&self, service: S) -> Self::Service { SessionService { service, - session_manager: self.session_manager.clone() + session_manager: self.session_manager.clone(), } } } @@ -48,12 +48,13 @@ pub struct SessionService { impl Service> for SessionService where S: Service, Response = Response>, - S::Future: Send+'static, - S::Response: Send+'static, + S::Future: Send + 'static, + S::Response: Send + 'static, { type Response = Response; type Error = S::Error; - type Future= Pin>+Send+'static>>; + type Future = + Pin> + Send + 'static>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.service.poll_ready(cx) @@ -66,33 +67,33 @@ where let cookies = req.headers().get("cookie").unwrap(); println!("the cookies:{:?}", cookies.to_str()); let cookies = cookies.to_str().unwrap(); - let should_redirect = if cookies.contains("sessionid") { - let session_id = cookies.split(';') - .map(|s| s.trim()) - .find(|s| s.starts_with("sessionid=")) - .and_then(|s| s.split_once('=')) - .map(|(_, v)| v).unwrap(); + let should_redirect = if cookies.contains("sessionid") { + let session_id = cookies + .split(';') + .map(|s| s.trim()) + .find(|s| s.starts_with("sessionid=")) + .and_then(|s| s.split_once('=')) + .map(|(_, v)| v) + .unwrap(); println!("the sessionid:{:?}", session_id); - if self.session_manager.is_session_exist(&session_id){ - if self.session_manager.is_expired(&session_id){ + if self.session_manager.is_session_exist(&session_id) { + if self.session_manager.is_expired(&session_id) { self.session_manager.remove_session(&session_id); true - }else{ + } else { false } - }else{ + } else { true } - - - }else{ + } else { true }; if should_redirect { println!("Returning response"); let response = Redirect::temporary("/tologin"); - return Box::pin(async {Ok(response.into_response())}); + return Box::pin(async { Ok(response.into_response()) }); }; } Box::pin(self.service.call(req)) diff --git a/src/lib.rs b/src/lib.rs index e9d650d..a211fc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,3 @@ -//pub mod service; -//pub mod traits; -//pub mod common; pub mod configuration; pub mod entity; pub mod job; diff --git a/src/main.rs b/src/main.rs index ca62990..209ab4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,22 +2,21 @@ use std::fs; use std::sync::atomic::AtomicU16; use std::sync::{Arc, RwLock}; +use axum::body::Body; use axum::extract::Multipart; -use axum::Form; +use axum::http::{Request, Response}; use axum::response::Html; use axum::response::IntoResponse; -use axum::http::{Request,Response}; -use axum::body::Body; - +use axum::Form; +use axum::response::Redirect; use axum::{ extract::Path, extract::State, + http::{StatusCode, Uri}, routing::{get, post}, - http::{StatusCode,Uri}, Json, Router, }; -use axum::response::Redirect; use axum_hello::entity::article_list_view::ArticlesListView; use axum_hello::layer::log_layer::LogLayer; use axum_hello::layer::session_layer::SessionLayer; @@ -39,17 +38,20 @@ pub mod configuration; pub mod entity; pub mod job; pub mod person; +pub mod session; use crate::configuration::app_config::AppConfig; use crate::entity::article::Article; use crate::entity::article_detail::ArticleDetail; use crate::entity::article_summary::ArticleSummary; use crate::entity::article_summary_vec::ArticleSummaryVec; -use crate::person::Person; use crate::entity::login_form::LoginForm; +use crate::person::Person; use crate::job::file_extractor::Extractor; +use crate::session::{session::Session, session_manage::SessionManager}; + #[tokio::main] async fn main() { //读取配置文件 @@ -61,15 +63,7 @@ async fn main() { let workspace = std::env::current_dir().unwrap(); println!("{:?}", &workspace); info!("Current workspace:: {:?}", &workspace.to_string_lossy()); - /* - let args: Vec = std::env::args().collect(); - if (args).capacity() < 2 { - //第一个参数是程序名称,第二个参数才是我们输入的参数 - eprintln!("You need to entry the basic_path"); - panic!("Lost the param basic_path"); - } - let basic_path = Arc::new(args[1].clone()); - */ + let pool = MySqlPoolOptions::new() .connect(&app_config.get_database_connection_url()) .await @@ -101,7 +95,7 @@ async fn main() { .fallback(status_sode_404) .layer( //设置同源策略 - CorsLayer::new().allow_origin("*".parse::().unwrap()),//need to fix + CorsLayer::new().allow_origin("*".parse::().unwrap()), //need to fix ) .layer(log_layer) .layer(url_layer) @@ -270,19 +264,30 @@ async fn to_login() -> Html { Html(login_page) } -async fn login(Form(form):Form) -> Result, StatusCode> { +async fn login(Form(form): Form) -> Result, StatusCode> { println!("login"); let app_config = AppConfig::get(); println!("app_config: {:?}", app_config); let admin = app_config.get_admin_settings(); if admin.username == form.username && admin.password == form.password { - let mut response = Redirect::to("/").into_response();//redirect to home page + let mut response = Redirect::to("/").into_response(); //redirect to home page let now: DateTime = Utc::now(); let thirty_minutes_later = now + Duration::minutes(30); let expire_time = thirty_minutes_later.format("%a, %d %b %Y %T GMT"); let session_id = Uuid::new_v4().to_string(); - let cookies = format!("sessionid={};Expires={}",session_id, expire_time); - response.headers_mut().insert("Set-Cookie", HeaderValue::from_str(cookies.as_str()).unwrap()); + let cookies = format!("sessionid={};Expires={}", session_id, expire_time); + + let session = Session::new( + session_id.clone(), + admin.username.clone(), + admin.password.clone(), + String::from("Admin"), + ); + + response.headers_mut().insert( + "Set-Cookie", + HeaderValue::from_str(cookies.as_str()).unwrap(), + ); return Ok(response); } return Err(StatusCode::UNAUTHORIZED); diff --git a/src/session/session_manage.rs b/src/session/session_manage.rs index 283a4b8..694b2c2 100644 --- a/src/session/session_manage.rs +++ b/src/session/session_manage.rs @@ -1,7 +1,10 @@ use std::collections::HashMap; use std::sync::Arc; +use std::sync::OnceLock; use std::sync::RwLock; +use tklog::error; + use crate::session::session::Session; #[derive(Clone, Debug)] @@ -13,13 +16,35 @@ pub struct SessionManager { const DEFAULT_TIMEOUT: u64 = 1800; //默认时间为1800秒 const DEFAULT_SESSION_COUNT: usize = 1; +// 使用 OnceLock 实现配置的单例缓存 +static SESSION_CONFIG: OnceLock = OnceLock::new(); + impl SessionManager { - pub fn new(timeout: Option) -> Self { + //因为使用了OnceLock, 那么要确保全局只能调用该方法一次,且只能调用一次 + pub fn init(timeout: Option) -> Result<(), Box> { let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); - SessionManager { + let session_manager = SessionManager { timeout, sessions: Arc::new(RwLock::new(HashMap::with_capacity(DEFAULT_SESSION_COUNT))), //只有管理员需要登陆 + }; + + match SESSION_CONFIG.set(session_manager) { + Ok(_) => { + println!("SessionManager has initialized successfully"); + } + Err(_) => { + let err_msg = "Session Manager already initialized"; + error!("{}", err_msg); + return Err(Box::from(err_msg)); + } } + Ok(()) + } + + pub fn get() -> &'static SessionManager { + SESSION_CONFIG + .get() + .expect("SessionManager not initialized") } pub fn add_session(&mut self, id: String, session: Session) { -- Gitee From abbcd9f1ba90e14728ab9ecc099e602864a0e940 Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 24 Jul 2025 16:22:02 +0800 Subject: [PATCH 17/20] login and session function --- src/layer/session_layer.rs | 16 +++++++---- src/main.rs | 11 ++++++-- src/session/session_manage.rs | 41 +++++++++++++-------------- src/session/session_manage_bk.rs | 48 ++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 30 deletions(-) create mode 100644 src/session/session_manage_bk.rs diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index e3c48d5..814af52 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -7,9 +7,12 @@ use std::{ pin::Pin, task::{Context, Poll}, }; +use tklog::info; + use crate::session::session_manage::SessionManager; + /// a demo https://tower-rs.github.io/tower/tower_layer/trait.Layer.html use tower_layer::Layer; use tower_service::Service; @@ -19,11 +22,11 @@ pub struct SessionLayer<'a> { session_manager: &'a SessionManager, } -impl SessionLayer<'_> { - pub fn new(timeout: Option) -> Self { - SessionManager::init(timeout).expect("Initialized SessionManager failed!"); - let session_manager = SessionManager::get(); - SessionLayer { session_manager } +impl<'a> SessionLayer<'a> { + pub fn new(session_manager: &'a SessionManager) -> Self { + SessionLayer { + session_manager + } } } @@ -82,12 +85,15 @@ where self.session_manager.remove_session(&session_id); true } else { + info!("Session is valid"); false } } else { + info!("Session does not exist"); true } } else { + info!("No session found"); true }; if should_redirect { diff --git a/src/main.rs b/src/main.rs index 209ab4a..b4e0327 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,10 @@ use axum_hello::layer::log_layer::LogLayer; use axum_hello::layer::session_layer::SessionLayer; use axum_hello::layer::url_layer::UrlLayer; use axum_hello::scheduler_job::article_read_time_scheduler_job::ArticleReadTimeSchedulerJob; +use axum_hello::session::session_manage::SessionManager; +use axum_hello::session::session::Session; + + use axum::http::HeaderValue; use sqlx::mysql::MySqlPoolOptions; @@ -50,7 +54,7 @@ use crate::person::Person; use crate::job::file_extractor::Extractor; -use crate::session::{session::Session, session_manage::SessionManager}; + #[tokio::main] async fn main() { @@ -80,7 +84,8 @@ async fn main() { target: "hello layer", }; let timeout = AppConfig::get().get_session_timeout(); - let session_layer = SessionLayer::new(timeout); + let _ = SessionManager::init(timeout).expect("Initialized SessionManager failed!"); + let session_layer = SessionLayer::new(SessionManager::get()); let router = Router::new() .route("/blog/new", post(upload_file)) .route("/toupload", get(upload_page)) @@ -101,7 +106,6 @@ async fn main() { .layer(url_layer) .layer(session_layer) .with_state(pool.clone()) - .with_state(app_config.clone()) .nest_service("/assets", tower_http::services::ServeDir::new("assets")); //开启scheudler @@ -284,6 +288,7 @@ async fn login(Form(form): Form) -> Result, StatusCode String::from("Admin"), ); + SessionManager::get().add_session(session_id.clone(), session).unwrap(); response.headers_mut().insert( "Set-Cookie", HeaderValue::from_str(cookies.as_str()).unwrap(), diff --git a/src/session/session_manage.rs b/src/session/session_manage.rs index 694b2c2..0003296 100644 --- a/src/session/session_manage.rs +++ b/src/session/session_manage.rs @@ -16,40 +16,37 @@ pub struct SessionManager { const DEFAULT_TIMEOUT: u64 = 1800; //默认时间为1800秒 const DEFAULT_SESSION_COUNT: usize = 1; -// 使用 OnceLock 实现配置的单例缓存 -static SESSION_CONFIG: OnceLock = OnceLock::new(); +static SESSION_MANAGER: OnceLock = OnceLock::new(); + impl SessionManager { - //因为使用了OnceLock, 那么要确保全局只能调用该方法一次,且只能调用一次 pub fn init(timeout: Option) -> Result<(), Box> { let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); - let session_manager = SessionManager { + let manager = SessionManager { timeout, - sessions: Arc::new(RwLock::new(HashMap::with_capacity(DEFAULT_SESSION_COUNT))), //只有管理员需要登陆 + sessions: Arc::new(RwLock::new(HashMap::new())), }; - match SESSION_CONFIG.set(session_manager) { - Ok(_) => { - println!("SessionManager has initialized successfully"); - } - Err(_) => { - let err_msg = "Session Manager already initialized"; - error!("{}", err_msg); - return Err(Box::from(err_msg)); - } - } - Ok(()) + SESSION_MANAGER.set(manager).map_err(|_| "Already initialized".into()) } pub fn get() -> &'static SessionManager { - SESSION_CONFIG - .get() - .expect("SessionManager not initialized") + SESSION_MANAGER.get().expect("SessionManager not initialized") } - pub fn add_session(&mut self, id: String, session: Session) { - self.sessions.write().unwrap().insert(id, session); - } + + + + pub fn add_session(&self, id: String, session: Session) -> Result<(), Box> { + let mut sessions = self.sessions.write() + .map_err(|e| Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to acquire write lock: {}", e) + )))?; + + sessions.insert(id, session); + Ok(()) + } pub fn remove_session(&mut self, id: &str) { self.sessions.write().unwrap().remove(id); diff --git a/src/session/session_manage_bk.rs b/src/session/session_manage_bk.rs new file mode 100644 index 0000000..d7a3f9f --- /dev/null +++ b/src/session/session_manage_bk.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; +use std::sync::Arc; +use std::sync::OnceLock; +use std::sync::RwLock; + +use tklog::error; + +use crate::session::session::Session; + +#[derive(Clone, Debug)] +pub struct SessionManager { + timeout: u64, + sessions: Arc>>, //hashmap线程不安全, Arc 允许多线程同时访问, 但需要使用RwLock来保证线程安全 +} + +const DEFAULT_TIMEOUT: u64 = 1800; //默认时间为1800秒 +const DEFAULT_SESSION_COUNT: usize = 1; + +impl SessionManager { + pub fn init(timeout: Option) -> Result> { + let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); + let session_manager = SessionManager { + timeout, + sessions: Arc::new(RwLock::new(HashMap::with_capacity(DEFAULT_SESSION_COUNT))), //只需要管理员需要登陆 + }; + Ok(session_manager) + } + + pub fn add_session(&mut self, id: String, session: Session) { + self.sessions.write().unwrap().insert(id, session); + } + + pub fn remove_session(&mut self, id: &str) { + self.sessions.write().unwrap().remove(id); + } + + pub fn is_session_exist(&self, id: &str) -> bool { + self.sessions.read().unwrap().contains_key(id) + } + + pub fn is_expired(&self, id: &str) -> bool { + self.sessions + .read() + .unwrap() + .get(id) + .map_or(false, |session| session.is_expired(self.timeout)) + } +} -- Gitee From 2152d98f59ffcdffc76c75de68ab0ac53c73f9cf Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 24 Jul 2025 17:20:04 +0800 Subject: [PATCH 18/20] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layer/session_layer.rs | 10 +++++----- src/session/session_manage.rs | 25 +++++++++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 814af52..0b9ff86 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -65,10 +65,10 @@ where fn call(&mut self, req: Request) -> Self::Future { let uri = req.uri().path(); - println!("Request URI: {}", uri); + info!("Request URI: {}", uri); if uri == "/toupload" { let cookies = req.headers().get("cookie").unwrap(); - println!("the cookies:{:?}", cookies.to_str()); + info!("the cookies:{:?}", cookies.to_str()); let cookies = cookies.to_str().unwrap(); let should_redirect = if cookies.contains("sessionid") { let session_id = cookies @@ -78,11 +78,11 @@ where .and_then(|s| s.split_once('=')) .map(|(_, v)| v) .unwrap(); - println!("the sessionid:{:?}", session_id); + info!("the sessionid:{:?}", session_id); if self.session_manager.is_session_exist(&session_id) { if self.session_manager.is_expired(&session_id) { - self.session_manager.remove_session(&session_id); + let _ = self.session_manager.remove_session(&session_id); true } else { info!("Session is valid"); @@ -97,7 +97,7 @@ where true }; if should_redirect { - println!("Returning response"); + info!("Returning response"); let response = Redirect::temporary("/tologin"); return Box::pin(async { Ok(response.into_response()) }); }; diff --git a/src/session/session_manage.rs b/src/session/session_manage.rs index 0003296..c1163ab 100644 --- a/src/session/session_manage.rs +++ b/src/session/session_manage.rs @@ -48,13 +48,23 @@ impl SessionManager { Ok(()) } - pub fn remove_session(&mut self, id: &str) { - self.sessions.write().unwrap().remove(id); - } - pub fn is_session_exist(&self, id: &str) -> bool { - self.sessions.read().unwrap().contains_key(id) - } + + pub fn remove_session(&self, id: &str) -> Result<(), Box> { + let mut sessions = self.sessions.write() + .map_err(|e| Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to acquire write lock: {}", e) + )))?; + + sessions.remove(id); + Ok(()) + } + + pub fn is_session_exist(&self, id: &str) -> bool { + self.sessions.read() + .map_or(false, |s| s.contains_key(id)) + } pub fn is_expired(&self, id: &str) -> bool { self.sessions @@ -63,4 +73,7 @@ impl SessionManager { .get(id) .map_or(false, |session| session.is_expired(self.timeout)) } + + + } -- Gitee From c0a342574f2df58255f9a1e37f47b3ce1714e6dd Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 24 Jul 2025 17:24:49 +0800 Subject: [PATCH 19/20] fmt code --- src/layer/session_layer.rs | 6 +--- src/main.rs | 10 +++--- src/session/session_manage.rs | 68 +++++++++++++++++------------------ 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 0b9ff86..8b38368 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -9,10 +9,8 @@ use std::{ }; use tklog::info; - use crate::session::session_manage::SessionManager; - /// a demo https://tower-rs.github.io/tower/tower_layer/trait.Layer.html use tower_layer::Layer; use tower_service::Service; @@ -24,9 +22,7 @@ pub struct SessionLayer<'a> { impl<'a> SessionLayer<'a> { pub fn new(session_manager: &'a SessionManager) -> Self { - SessionLayer { - session_manager - } + SessionLayer { session_manager } } } diff --git a/src/main.rs b/src/main.rs index b4e0327..6fe0ad6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,10 +22,8 @@ use axum_hello::layer::log_layer::LogLayer; use axum_hello::layer::session_layer::SessionLayer; use axum_hello::layer::url_layer::UrlLayer; use axum_hello::scheduler_job::article_read_time_scheduler_job::ArticleReadTimeSchedulerJob; -use axum_hello::session::session_manage::SessionManager; use axum_hello::session::session::Session; - - +use axum_hello::session::session_manage::SessionManager; use axum::http::HeaderValue; use sqlx::mysql::MySqlPoolOptions; @@ -54,8 +52,6 @@ use crate::person::Person; use crate::job::file_extractor::Extractor; - - #[tokio::main] async fn main() { //读取配置文件 @@ -288,7 +284,9 @@ async fn login(Form(form): Form) -> Result, StatusCode String::from("Admin"), ); - SessionManager::get().add_session(session_id.clone(), session).unwrap(); + SessionManager::get() + .add_session(session_id.clone(), session) + .unwrap(); response.headers_mut().insert( "Set-Cookie", HeaderValue::from_str(cookies.as_str()).unwrap(), diff --git a/src/session/session_manage.rs b/src/session/session_manage.rs index c1163ab..16c0fdf 100644 --- a/src/session/session_manage.rs +++ b/src/session/session_manage.rs @@ -18,7 +18,6 @@ const DEFAULT_SESSION_COUNT: usize = 1; static SESSION_MANAGER: OnceLock = OnceLock::new(); - impl SessionManager { pub fn init(timeout: Option) -> Result<(), Box> { let timeout = timeout.unwrap_or(DEFAULT_TIMEOUT); @@ -27,44 +26,48 @@ impl SessionManager { sessions: Arc::new(RwLock::new(HashMap::new())), }; - SESSION_MANAGER.set(manager).map_err(|_| "Already initialized".into()) + SESSION_MANAGER + .set(manager) + .map_err(|_| "Already initialized".into()) } pub fn get() -> &'static SessionManager { - SESSION_MANAGER.get().expect("SessionManager not initialized") + SESSION_MANAGER + .get() + .expect("SessionManager not initialized") } - - - - pub fn add_session(&self, id: String, session: Session) -> Result<(), Box> { - let mut sessions = self.sessions.write() - .map_err(|e| Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Failed to acquire write lock: {}", e) - )))?; - - sessions.insert(id, session); - Ok(()) - } - - + pub fn add_session( + &self, + id: String, + session: Session, + ) -> Result<(), Box> { + let mut sessions = self.sessions.write().map_err(|e| { + Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to acquire write lock: {}", e), + )) + })?; + + sessions.insert(id, session); + Ok(()) + } pub fn remove_session(&self, id: &str) -> Result<(), Box> { - let mut sessions = self.sessions.write() - .map_err(|e| Box::new(std::io::Error::new( - std::io::ErrorKind::Other, - format!("Failed to acquire write lock: {}", e) - )))?; - - sessions.remove(id); - Ok(()) - } + let mut sessions = self.sessions.write().map_err(|e| { + Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + format!("Failed to acquire write lock: {}", e), + )) + })?; + + sessions.remove(id); + Ok(()) + } - pub fn is_session_exist(&self, id: &str) -> bool { - self.sessions.read() - .map_or(false, |s| s.contains_key(id)) - } + pub fn is_session_exist(&self, id: &str) -> bool { + self.sessions.read().map_or(false, |s| s.contains_key(id)) + } pub fn is_expired(&self, id: &str) -> bool { self.sessions @@ -73,7 +76,4 @@ impl SessionManager { .get(id) .map_or(false, |session| session.is_expired(self.timeout)) } - - - } -- Gitee From c5f2e3b446f03659eff71d139db63ab25784502e Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 24 Jul 2025 17:38:41 +0800 Subject: [PATCH 20/20] =?UTF-8?q?markdown=20=E8=BF=98=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/layer/session_layer.rs | 2 +- templates/html/blog.html | 92 +++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/layer/session_layer.rs b/src/layer/session_layer.rs index 8b38368..505d6e9 100644 --- a/src/layer/session_layer.rs +++ b/src/layer/session_layer.rs @@ -64,7 +64,7 @@ where info!("Request URI: {}", uri); if uri == "/toupload" { let cookies = req.headers().get("cookie").unwrap(); - info!("the cookies:{:?}", cookies.to_str()); + println!("the cookies:{:?}", cookies.to_str()); let cookies = cookies.to_str().unwrap(); let should_redirect = if cookies.contains("sessionid") { let session_id = cookies diff --git a/templates/html/blog.html b/templates/html/blog.html index 4ce5227..be65f7c 100644 --- a/templates/html/blog.html +++ b/templates/html/blog.html @@ -5,8 +5,15 @@ RustU - - + + @@ -85,19 +91,28 @@

-
+

- 粤公网安备44011802000978号 ICP备案: 粤ICP备2024315825号-1 © 2024 你我同锈. All rights reserved. + 粤公网安备44011802000978号 ICP备案: 粤ICP备2024315825号-1 + © 2024 你我同锈. All rights reserved.

- + + -- Gitee