增加google账户信息

This commit is contained in:
李运家 2024-05-23 17:43:46 +08:00
parent cbfc70357e
commit 638226a22e
5 changed files with 224 additions and 18 deletions

36
library/src/errors.rs Normal file
View File

@ -0,0 +1,36 @@
use core::fmt;
use std::error::Error;
#[derive(Debug)]
pub struct MessageError(Option<String>);
// 实现Error trait
impl Error for MessageError {
fn cause(&self) -> Option<&dyn Error> {
self.source()
}
}
// 实现Display trait这是Error trait的一部分要求
impl fmt::Display for MessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Some(message) => write!(f, "{}", message),
None => write!(f, "")
}
}
}
impl From<String> for MessageError {
fn from(value: String) -> Self {
MessageError(Some(value))
}
}
impl From<&str> for MessageError {
fn from(value: &str) -> Self {
MessageError(Some(value.to_string()))
}
}

View File

@ -5,4 +5,5 @@ pub mod resp;
pub mod middleware;
pub mod token;
pub mod cache;
pub mod social;
pub mod social;
pub mod errors;

View File

@ -1,3 +1,5 @@
use std::{error::Error as StdError, fmt::Display};
use axum::{
response::{IntoResponse, Response},
Json,
@ -22,6 +24,7 @@ where
}
}
#[derive(Debug)]
pub enum ResErr {
Error(i32, String),
ErrParams(Option<String>),
@ -31,6 +34,7 @@ pub enum ResErr {
ErrSystem(Option<String>),
ErrData(Option<String>),
ErrService(Option<String>),
ErrSocial(Option<String>),
}
use ResErr::*;
@ -95,10 +99,81 @@ impl IntoResponse for ResErr {
None => Status::<()>::Err(code, String::from("服务异常")),
}
}
ErrSocial(msg) => {
let code = 80000;
match msg {
Some(v) => Status::<()>::Err(code, v),
None => Status::<()>::Err(code, String::from("社交服务异常")),
}
}
};
Json(status.to_reply()).into_response()
}
}
// impl Display for ResErr {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// // write!(f, "{:?}", self.message)
// match self {
// Error(value, message) => write!(f, "{} - {:?}", value, message),
// ErrParams(message)
// | ErrAuth(message)
// | ErrPerm(message)
// | ErrNotFound(message)
// | ErrSystem(message)
// | ErrData(message)
// | ErrService(message)
// | ErrSocial(message) => write!(f, "{:?}", message),
// }
// }
// }
// impl StdError for ResErr {
// fn cause(&self) -> Option<&dyn StdError> {
// match self {
// Error(_, _) => None,
// ErrParams(_)
// | ErrAuth(_)
// | ErrPerm(_)
// | ErrNotFound(_)
// | ErrSystem(_)
// | ErrData(_)
// | ErrService(_)
// | ErrSocial(_) => None,
// }
// }
// fn source(&self) -> Option<&(dyn StdError + 'static)> {
// None
// }
// fn description(&self) -> &str {
// match self {
// Error(value, message) => &format!("Error, {} - {}", value, message),
// ErrParams(message) => &format!("ErrParams, {}", message),
// ErrAuth(message) => &format!("ErrAuth, {}", message),
// ErrPerm(message) => &format!("ErrPerm, {}", message),
// ErrNotFound(message) => &format!("ErrNotFound, {}", message),
// ErrSystem(message) => &format!("ErrSystem, {}", message),
// ErrData(message) => &format!("ErrData, {}", message),
// ErrService(message) => &format!("ErrService, {}", message),
// ErrSocial(message) => &format!("ErrSocial, {}", message),
// }
// }
// fn provide<'a>(&'a self, request: &mut std::error::Request<'a>) {
// match self {
// ResErr::ErrSystem(e) => request.provide_ref(e),
// ResErr::ErrData(e) => request.provide_ref(e),
// ResErr::ErrService(e) => request.provide_ref(e),
// ResErr::ErrSocial(e) => request.provide_ref(e),
// ResErr::ErrNotFound(e) => request.provide_ref(e),
// ResErr::ErrPerm(e) => request.provide_ref(e),
// ResErr::ErrAuth(e) => request.provide_ref(e),
// ResErr::ErrParams(e) => request.provide_ref(e),
// ResErr::Error(_, e) => request.provide_ref(e),
// }
// }
pub type ResResult<T> = std::result::Result<T, ResErr>;

View File

@ -1,14 +1,99 @@
use std::collections::HashMap;
use std::{collections::HashMap, fmt::Error};
use jsonwebtoken::{decode, errors::{Error, ErrorKind}, Algorithm, DecodingKey, TokenData, Validation};
use chrono::Utc;
use jsonwebtoken::{decode, errors::ErrorKind, Algorithm, DecodingKey, TokenData, Validation};
use reqwest::Client;
use serde_json::Value;
type SocialResult<T> = Result<T, Box<dyn std::error::Error>>;
use crate::{errors::MessageError, resp::response::ResErr};
use super::SocialResult;
// 假设GOOGLE_PUBLIC_CERT_URL是Google提供的公钥URL
const GOOGLE_PUBLIC_CERT_URL: &str = "https://www.googleapis.com/oauth2/v3/certs";
#[derive(Debug, Default)]
pub struct GoogleJwtProfile {
// iss (issuer):签发人
pub iss: String,
// sub (subject):主题
pub sub: String,
pub azp: String,
// aud (audience):受众
pub aud: String,
// iat (Issued At):签发时间
pub iat: i64,
// exp (expiration time):过期时间
pub exp: i64,
pub email: String,
pub email_verified: bool,
pub at_hash: String,
pub name: String,
pub picture: String,
pub given_name: String,
pub family_name: String,
pub locale: String,
}
impl GoogleJwtProfile {
fn new() -> Self {
GoogleJwtProfile{
..Default::default()
}
}
}
impl From<Value> for GoogleJwtProfile {
fn from(value: Value) -> Self {
let mut google_jwt_profile = GoogleJwtProfile::new();
if let Some(value) = value.get("iss") {
google_jwt_profile.iss = value.to_string();
}
if let Some(value) = value.get("sub") {
google_jwt_profile.sub = value.to_string();
}
if let Some(value) = value.get("azp") {
google_jwt_profile.azp = value.to_string();
}
if let Some(value) = value.get("aud") {
google_jwt_profile.aud = value.to_string();
}
if let Some(value) = value.get("iat") {
google_jwt_profile.iat = value.as_i64().unwrap_or_default();
}
if let Some(value) = value.get("exp") {
google_jwt_profile.exp = value.as_i64().unwrap_or_default();
}
if let Some(value) = value.get("email") {
google_jwt_profile.email = value.to_string();
}
if let Some(value) = value.get("email_verified") {
google_jwt_profile.email_verified = value.as_bool().unwrap_or_default();
}
if let Some(value) = value.get("at_hash") {
google_jwt_profile.at_hash = value.to_string();
}
if let Some(value) = value.get("name") {
google_jwt_profile.name = value.to_string();
}
if let Some(value) = value.get("picture") {
google_jwt_profile.picture = value.to_string();
}
if let Some(value) = value.get("given_name") {
google_jwt_profile.given_name = value.to_string();
}
if let Some(value) = value.get("family_name") {
google_jwt_profile.family_name = value.to_string();
}
if let Some(value) = value.get("locale") {
google_jwt_profile.locale = value.to_string();
}
google_jwt_profile
}
}
// 异步获取并解析Google公钥
async fn fetch_and_parse_google_public_keys() -> SocialResult<HashMap<String, String>> {
let response = Client::new().get(GOOGLE_PUBLIC_CERT_URL).send().await?;
@ -27,7 +112,7 @@ async fn fetch_and_parse_google_public_keys() -> SocialResult<HashMap<String, St
}
// 验证ID Token并考虑kid匹配
async fn verify_id_token(id_token: &str) -> SocialResult<Value> {
async fn verify_id_token(id_token: &str) -> SocialResult<GoogleJwtProfile> {
// 获取并解析公钥
let public_keys = fetch_and_parse_google_public_keys().await?;
@ -37,28 +122,36 @@ async fn verify_id_token(id_token: &str) -> SocialResult<Value> {
// 检查是否找到了有效的kid
if kid.is_none() {
return Err(Box::new(Error::from(ErrorKind::InvalidToken)));
return Err(Box::new(MessageError::from("校验Token失败,未找到有效的kid")));
}
let kid = kid.unwrap();
// 根据kid找到正确的公钥
let key = public_keys.get(&kid).ok_or_else(|| Box::new(Error::from(ErrorKind::InvalidToken)))?;
let key = public_keys.get(&kid).ok_or_else(|| Box::new(MessageError::from("校验Token失败,未找到正确的公钥")))?;
// 验证Token
let mut validation: Validation = Validation::new(Algorithm::RS256);
validation.set_issuer(&["https://accounts.google.com"]);// 设置预期的发行者
validation.set_issuer(&["https://accounts.google.com", "accounts.google.com"]);// 设置预期的发行者
let decoded = decode::<Value>(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)
let claims: Value = decoded.claims;
let google_jwt_profile = GoogleJwtProfile::from(claims);
// 校验有效期
if google_jwt_profile.exp < Utc::now().timestamp() {
return Err(Box::new(MessageError::from("校验Token失败,token有效期无效")));
}
// 校验签发时间
// if google_jwt_profile.iat > Utc::now().timestamp() {
// return Err(Box::new(Error::from(ErrorKind::InvalidToken)));
// }
// if google_jwt_profile.aud != config::GOOGLE_CLIENT_ID {
// }
// 校验iss字段
if google_jwt_profile.iss != "accounts.google.com" && google_jwt_profile.iss != "https://accounts.google.com" {
return Err(Box::new(MessageError::from("校验Token失败,token签发人非法")));
}
Ok(google_jwt_profile)
}

View File

@ -1,3 +1,4 @@
type SocialResult<T> = Result<T, Box<dyn std::error::Error>>;
pub mod google;
pub mod facebook;