From b6032bc286af6db8513e9dc4fc646b463b9ecf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=BF=90=E5=AE=B6?= Date: Thu, 23 May 2024 15:30:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0google=E8=B4=A6=E5=8F=B7=20id?= =?UTF-8?q?=20token=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 2 + library/Cargo.toml | 3 +- library/src/{core/auth_cache.rs => cache.rs} | 0 library/src/core/mod.rs | 1 - library/src/lib.rs | 4 +- library/src/social/google.rs | 64 ++++++++++++++++++++ library/src/social/mod.rs | 2 + 7 files changed, 73 insertions(+), 3 deletions(-) rename library/src/{core/auth_cache.rs => cache.rs} (100%) create mode 100644 library/src/social/google.rs create mode 100644 library/src/social/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 6890e55..a142ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1065,6 +1065,7 @@ dependencies = [ "jsonwebtoken", "moka", "once_cell", + "reqwest", "serde", "serde_json", "sqlx", @@ -1639,6 +1640,7 @@ dependencies = [ "base64 0.22.0", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", diff --git a/library/Cargo.toml b/library/Cargo.toml index ccc798f..c035d27 100644 --- a/library/Cargo.toml +++ b/library/Cargo.toml @@ -24,4 +24,5 @@ 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 } \ No newline at end of file +jsonwebtoken = { workspace = true } +reqwest = { workspace = true, features = ["blocking", "json"] } \ No newline at end of file diff --git a/library/src/core/auth_cache.rs b/library/src/cache.rs similarity index 100% rename from library/src/core/auth_cache.rs rename to library/src/cache.rs diff --git a/library/src/core/mod.rs b/library/src/core/mod.rs index 54ee721..c8911b9 100644 --- a/library/src/core/mod.rs +++ b/library/src/core/mod.rs @@ -2,4 +2,3 @@ pub mod config; pub mod logger; pub mod db; pub mod local_cache; -pub mod auth_cache; diff --git a/library/src/lib.rs b/library/src/lib.rs index 5b1a317..8db8ea3 100644 --- a/library/src/lib.rs +++ b/library/src/lib.rs @@ -3,4 +3,6 @@ extern crate self as library; pub mod core; pub mod resp; pub mod middleware; -pub mod token; \ No newline at end of file +pub mod token; +pub mod cache; +pub mod social; \ No newline at end of file diff --git a/library/src/social/google.rs b/library/src/social/google.rs new file mode 100644 index 0000000..d9980b1 --- /dev/null +++ b/library/src/social/google.rs @@ -0,0 +1,64 @@ +use std::collections::HashMap; + +use jsonwebtoken::{decode, errors::{Error, ErrorKind}, Algorithm, DecodingKey, TokenData, Validation}; +use reqwest::Client; +use serde_json::Value; + +type SocialResult = Result>; + +// 假设GOOGLE_PUBLIC_CERT_URL是Google提供的公钥URL +const GOOGLE_PUBLIC_CERT_URL: &str = "https://www.googleapis.com/oauth2/v3/certs"; + +// 异步获取并解析Google公钥 +async fn fetch_and_parse_google_public_keys() -> SocialResult> { + let response = Client::new().get(GOOGLE_PUBLIC_CERT_URL).send().await?; + let google_keys: Value = response.json().await?; + + let mut key_map = HashMap::new(); + if let Value::Object(keys_obj) = google_keys { + for (kid, key_val) in keys_obj { + if let Value::String(key) = key_val { + key_map.insert(kid, key); + } + } + } + + Ok(key_map) +} + +// 验证ID Token并考虑kid匹配 +async fn verify_id_token(id_token: &str) -> SocialResult { + // 获取并解析公钥 + let public_keys = fetch_and_parse_google_public_keys().await?; + + // 解码Token头部以获取kid + let token_header = jsonwebtoken::decode_header(id_token).unwrap(); + let kid = token_header.kid; + + // 检查是否找到了有效的kid + if kid.is_none() { + return Err(Box::new(Error::from(ErrorKind::InvalidToken))); + } + let kid = kid.unwrap(); + + // 根据kid找到正确的公钥 + let key = public_keys.get(&kid).ok_or_else(|| Box::new(Error::from(ErrorKind::InvalidToken)))?; + + // 验证Token + let mut validation: Validation = Validation::new(Algorithm::RS256); + validation.set_issuer(&["https://accounts.google.com"]);// 设置预期的发行者 + + let decoded = decode::(id_token, &DecodingKey::from_rsa_pem(key.as_bytes())?, &validation)?; + + let claims = decoded.claims; + // 进一步校验iss字段 + if let Some(issuer) = claims.get("iss") { + if issuer.as_str().unwrap() != "https://accounts.google.com" { + return Err(Box::new(Error::from(ErrorKind::InvalidToken))); + } + } else { + return Err(Box::new(Error::from(ErrorKind::InvalidToken))); + } + Ok(claims) + +} diff --git a/library/src/social/mod.rs b/library/src/social/mod.rs new file mode 100644 index 0000000..8c424f3 --- /dev/null +++ b/library/src/social/mod.rs @@ -0,0 +1,2 @@ + +pub mod google; \ No newline at end of file