From 837a296d21eceaf6bcce2c866c3dcbe1dfe2d9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=BF=90=E5=AE=B6?= Date: Thu, 30 May 2024 14:11:49 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 26 +++++++++--------- api/src/controller/account.rs | 2 +- app.toml | 3 ++- domain/src/dto/account.rs | 8 ++++++ domain/src/entities/account.rs | 22 ++++++++++++++++ library/Cargo.toml | 1 - library/src/cache.rs | 17 ------------ library/src/core/config.rs | 3 ++- library/src/lib.rs | 1 - library/src/middleware/req_token.rs | 2 +- library/src/token.rs | 41 +++++++++++++++++++---------- service/Cargo.toml | 2 ++ service/src/account.rs | 5 ++-- service/src/lib.rs | 3 +++ service/src/sys_account.rs | 19 +++++++++++++ service/src/utils/login_cache.rs | 24 +++++++++++++++++ service/src/utils/mod.rs | 1 + 17 files changed, 128 insertions(+), 52 deletions(-) delete mode 100644 library/src/cache.rs create mode 100644 service/src/sys_account.rs create mode 100644 service/src/utils/login_cache.rs create mode 100644 service/src/utils/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 959b6e2..50badb0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -308,9 +308,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -565,9 +565,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", @@ -1067,7 +1067,6 @@ dependencies = [ "http-body-util", "jsonwebtoken", "lazy_static", - "moka", "once_cell", "reqwest", "serde", @@ -1173,17 +1172,18 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87bfd249f570638bfb0b4f9d258e6b8cddd2a5a7d0ed47e8bb8b176bfc0e7a17" +checksum = "9e0d88686dc561d743b40de8269b26eaf0dc58781bde087b0984646602021d08" dependencies = [ "async-lock", "async-trait", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", - "event-listener 5.3.0", + "event-listener 5.3.1", "futures-util", + "log", "once_cell", "parking_lot", "quanta", @@ -1591,9 +1591,9 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "11.0.1" +version = "11.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d86a7c4638d42c44551f4791a20e687dbb4c3de1f33c43dd71e355cd429def1" +checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" dependencies = [ "bitflags 2.4.2", ] @@ -1924,7 +1924,9 @@ dependencies = [ "domain", "error-stack", "futures-executor", + "lazy_static", "library", + "moka", "reqwest", "sqlx", "tracing", @@ -2657,9 +2659,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3" +checksum = "1b2cb4fbb9995eeb36ac86fadf24031ccd58f99d6b4b2d7b911db70bddb80d90" [[package]] name = "try-lock" diff --git a/api/src/controller/account.rs b/api/src/controller/account.rs index 54649d4..a59fef4 100644 --- a/api/src/controller/account.rs +++ b/api/src/controller/account.rs @@ -9,7 +9,7 @@ pub async fn authenticate_google( ) -> ResResult> { req.validate()?; - service::account::authticate_google(req).await?; + service::account::authenticate_google(req).await?; Ok(ResOK(None)) } diff --git a/app.toml b/app.toml index d57a3d8..4067aed 100644 --- a/app.toml +++ b/app.toml @@ -13,6 +13,7 @@ url = "postgres://lyj:1325479Lyj!@47.95.198.7:13207/demo_rs" options = { min_conns = 10, max_conns = 20, conn_timeout = 30, idle_timeout = 300, max_lifetime = 60, sql_logging = true } [jwt] -secret = "chuanyue" +token_secret = "chuanyue" +refresh_token_secret = "chuanyue" expires = 1800 refresh_expires = 3600 \ No newline at end of file diff --git a/domain/src/dto/account.rs b/domain/src/dto/account.rs index c8209e7..0d3c841 100644 --- a/domain/src/dto/account.rs +++ b/domain/src/dto/account.rs @@ -1,6 +1,14 @@ use serde::{Deserialize, Serialize}; use validator::Validate; +#[derive(Debug, Validate, Deserialize, Serialize)] +pub struct AuthenticateWithPassword { + #[validate(required(message = "用户名不能为空"), length(min = 1, message = "用户名不能为空"))] + pub username: Option, + #[validate(required(message = "密码不能为空"), length(min = 1, message = "密码不能为空"))] + pub password: Option, +} + #[derive(Debug, Validate, Deserialize, Serialize)] pub struct AuthenticateGooleAccountReq { #[validate(required(message = "用户ID Token不能为空"), length(min = 1, message = "用户ID Token不能为空"))] diff --git a/domain/src/entities/account.rs b/domain/src/entities/account.rs index 727f918..985a43f 100644 --- a/domain/src/entities/account.rs +++ b/domain/src/entities/account.rs @@ -131,4 +131,26 @@ impl Account { self.avatar_url.clone().unwrap() ).fetch_one(db_pool).await } + + pub async fn find_with_password(username: String, password: String, db_pool: &PgPool) -> Result, Error> { + match sqlx::query_as!( + Account, + r#" + select * from account where username = $1 and password = $2 + "#, + username, + password.as_bytes() + ).fetch_one(db_pool).await { + Ok(account) => { + return Ok(Some(account)); + } + Err(Error::RowNotFound) => { + return Ok(None); + } + Err(e) => { + tracing::error!("find_by_google_id error: {:?}", e); + return Err(e); + } + } + } } \ No newline at end of file diff --git a/library/Cargo.toml b/library/Cargo.toml index 9e02e7b..3a58909 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -21,7 +21,6 @@ serde_json = { workspace = true } http = { workspace = true } http-body = { workspace = true } http-body-util = { workspace = true } -moka = { workspace = true, features = ["future"] } tokio = { workspace = true, features = ["rt-multi-thread", "macros" ] } futures-util = { workspace = true } jsonwebtoken = { workspace = true } diff --git a/library/src/cache.rs b/library/src/cache.rs deleted file mode 100644 index edf8fcf..0000000 --- a/library/src/cache.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::sync::OnceLock; -use moka::future::Cache; - -#[macro_export] -macro_rules! auth_cache { - () => { - library::core::auth_cache::init_auth_cache() - }; -} - -static AUTH_CACHE: OnceLock> = OnceLock::new(); - -pub fn init_auth_cache() -> &'static Cache { - AUTH_CACHE.get_or_init(|| { - Cache::new(1000) - }) -} diff --git a/library/src/core/config.rs b/library/src/core/config.rs index df543cc..5d2d856 100644 --- a/library/src/core/config.rs +++ b/library/src/core/config.rs @@ -42,7 +42,8 @@ pub struct Logger { #[derive(Clone, Debug, Deserialize)] pub struct Jwt { - pub secret: String, + pub token_secret: String, + pub refresh_token_secret: String, pub expires: i64, pub refresh_expires: i64, } diff --git a/library/src/lib.rs b/library/src/lib.rs index 8db8ea3..e0a4514 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -4,5 +4,4 @@ pub mod core; pub mod resp; pub mod middleware; pub mod token; -pub mod cache; pub mod social; \ No newline at end of file diff --git a/library/src/middleware/req_token.rs b/library/src/middleware/req_token.rs index 46afcbf..f9bd830 100644 --- a/library/src/middleware/req_token.rs +++ b/library/src/middleware/req_token.rs @@ -19,7 +19,7 @@ pub async fn authenticate_access_token(mut req: Request, next: Next) -> Response }; let validation = Validation::default(); - match decode::(token, &DecodingKey::from_secret(config!().jwt.secret.as_bytes()), &validation) { + match decode::(token, &DecodingKey::from_secret(config!().jwt.token_secret.as_bytes()), &validation) { Ok(decoded) => { // 将Claims附加到请求扩展中,以便后续处理使用 req.extensions_mut().insert(decoded.claims); diff --git a/library/src/token.rs b/library/src/token.rs index c6026d4..75b6020 100644 --- a/library/src/token.rs +++ b/library/src/token.rs @@ -14,22 +14,10 @@ pub fn generate_token(sub: &str) -> String { sub: sub.to_string(), exp: (Utc::now() + TimeDelta::try_seconds(config!().jwt.expires).unwrap()).timestamp(), }; - generate(claim) -} - -pub fn generate_refresh_token(sub: &str) -> String { - let claim = Claims { - sub: sub.to_string(), - exp: (Utc::now() + TimeDelta::try_seconds(config!().jwt.refresh_expires).unwrap()).timestamp(), - }; - generate(claim) -} - -fn generate(claim: Claims) -> String { let token = jsonwebtoken::encode( &jsonwebtoken::Header::default(), &claim, - &jsonwebtoken::EncodingKey::from_secret(config!().jwt.secret.as_bytes()), + &jsonwebtoken::EncodingKey::from_secret(config!().jwt.token_secret.as_bytes()), ); token.unwrap_or_else(|e| { tracing::error!(error =?e, "生成Token失败"); @@ -37,10 +25,35 @@ fn generate(claim: Claims) -> String { }) } +pub fn generate_refresh_token(sub: &str) -> String { + let claim = Claims { + sub: sub.to_string(), + exp: (Utc::now() + TimeDelta::try_seconds(config!().jwt.expires).unwrap()).timestamp(), + }; + let token = jsonwebtoken::encode( + &jsonwebtoken::Header::default(), + &claim, + &jsonwebtoken::EncodingKey::from_secret(config!().jwt.refresh_token_secret.as_bytes()), + ); + token.unwrap_or_else(|e| { + tracing::error!(error =?e, "生成Rfresh Token失败"); + "".to_string() + }) +} + pub fn verify_token(token: &str) -> Result { jsonwebtoken::decode::( token, - &jsonwebtoken::DecodingKey::from_secret(config!().jwt.secret.as_bytes()), + &jsonwebtoken::DecodingKey::from_secret(config!().jwt.token_secret.as_bytes()), + &jsonwebtoken::Validation::default(), + ) + .map(|data| data.claims) +} + +pub fn verify_refresh_token(token: &str) -> Result { + jsonwebtoken::decode::( + token, + &jsonwebtoken::DecodingKey::from_secret(config!().jwt.refresh_token_secret.as_bytes()), &jsonwebtoken::Validation::default(), ) .map(|data| data.claims) diff --git a/service/Cargo.toml b/service/Cargo.toml index 745fa06..98d4e99 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -12,6 +12,8 @@ reqwest = { workspace = true } futures-executor = { workspace = true } error-stack = { workspace = true } sqlx = { workspace = true, features = ["uuid"] } +moka = { workspace = true, features = ["future", "logging"] } +lazy_static = { workspace = true } library = { path = "../library" } domain = { path = "../domain" } \ No newline at end of file diff --git a/service/src/account.rs b/service/src/account.rs index 36988f5..10acfd8 100644 --- a/service/src/account.rs +++ b/service/src/account.rs @@ -1,14 +1,13 @@ use chrono::Utc; use domain::dto::account::AuthenticateGooleAccountReq; use domain::entities::account::Account; -use library::social::apple::APPLE_SOCIAL; use library::{db, token}; use library::resp::response::ResErr::ErrPerm; use library::resp::response::{ResErr, ResOK, ResResult}; use library::social::google::GOOGLE_SOCIAL; -pub async fn authticate_google(req: AuthenticateGooleAccountReq) -> ResResult> { - let verify_result = APPLE_SOCIAL.verify_id_token(&req.id_token.unwrap()).await.map_err(|err| { +pub async fn authenticate_google(req: AuthenticateGooleAccountReq) -> ResResult> { + let verify_result = GOOGLE_SOCIAL.verify_id_token(&req.id_token.unwrap()).await.map_err(|err| { tracing::error!(error = ?err, "校验Google Token失败"); ErrPerm(None) })?; diff --git a/service/src/lib.rs b/service/src/lib.rs index ed16296..775057a 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -1,2 +1,5 @@ pub mod account; pub mod feedback; +pub mod sys_account; + +pub mod utils; \ No newline at end of file diff --git a/service/src/sys_account.rs b/service/src/sys_account.rs new file mode 100644 index 0000000..16b6936 --- /dev/null +++ b/service/src/sys_account.rs @@ -0,0 +1,19 @@ +use chrono::Utc; +use domain::{dto::account::AuthenticateWithPassword, entities::account::Account}; +use library::{db, resp::response::{ResErr, ResOK, ResResult}}; + + +pub async fn authticate_with_password(req: AuthenticateWithPassword) -> ResResult> { + let account = Account::find_with_password(req.username.unwrap(), req.password.unwrap(), db!()).await?; + if account.is_none() { + tracing::info!("登录用户失败,用户查询为空"); + return Err(ResErr::params("用户名或密码错误")); + } + let account = account.unwrap(); + if account.disable_time > Utc::now() { + tracing::error!("账户已禁用"); + return Err(ResErr::auth("账户已禁用")); + } + + Ok(ResOK(Some(("".to_string(), "".to_string())))) +} \ No newline at end of file diff --git a/service/src/utils/login_cache.rs b/service/src/utils/login_cache.rs new file mode 100644 index 0000000..06e1564 --- /dev/null +++ b/service/src/utils/login_cache.rs @@ -0,0 +1,24 @@ +use std::time::Duration; + +use lazy_static::lazy_static; +use library::config; +use moka::{ + future::{Cache, CacheBuilder}, + policy::EvictionPolicy, +}; + +#[derive(Debug, Clone)] +pub struct LoginAccount {} + +lazy_static! { + pub static ref LOGIN_CACHE: Cache = { + CacheBuilder::new(20480) + .name("login_cache") + .eviction_policy(EvictionPolicy::lru()) + .time_to_live(Duration::from_secs(config!().jwt.expires as u64)) + .eviction_listener(|key, value, cause| { + tracing::info!("login_cache evict key: {:?}, value: {:?}, cause: {:?}", key, value, cause); + }) + .build() + }; +} diff --git a/service/src/utils/mod.rs b/service/src/utils/mod.rs new file mode 100644 index 0000000..ee743b8 --- /dev/null +++ b/service/src/utils/mod.rs @@ -0,0 +1 @@ +pub mod login_cache; \ No newline at end of file