完善request context中间件,错误信息使用i18n处理

This commit is contained in:
李运家 2024-09-27 18:52:32 +08:00
parent 461edb0f25
commit 0d8802b495
5 changed files with 26 additions and 8 deletions

View File

@ -11,4 +11,5 @@ ValidateAccountPasswordRequired,password is required,密码不能为空
ValidateAccountIdTokenRequired,ID Token is required,用户ID Token不能为空 ValidateAccountIdTokenRequired,ID Token is required,用户ID Token不能为空
ValidateAccountLangTagRequired,lang tag is required,用户语言标识不能为空 ValidateAccountLangTagRequired,lang tag is required,用户语言标识不能为空
ValidatePageablePageRequired,invalid page number,页码无效 ValidatePageablePageRequired,invalid page number,页码无效
ValidatePageablePageSizeRequired,invalid quantity per page,每页数量无效 ValidatePageablePageSizeRequired,invalid quantity per page,每页数量无效
BadRequest,bad request,无效请求
1 id en-US zh-CN
11 ValidateAccountIdTokenRequired ID Token is required 用户ID Token不能为空
12 ValidateAccountLangTagRequired lang tag is required 用户语言标识不能为空
13 ValidatePageablePageRequired invalid page number 页码无效
14 ValidatePageablePageSizeRequired invalid quantity per page 每页数量无效
15 BadRequest bad request 无效请求

View File

@ -2,7 +2,9 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumString};
#[derive(Debug, EnumIter, EnumString, Display, PartialEq, AsRefStr)] #[derive(Debug, EnumIter, EnumString, Display, PartialEq, AsRefStr)]
pub enum MessageId { pub enum MessageId {
BadRequest,
ServerInternalError, ServerInternalError,
Hello, Hello,
AccountDisabled, AccountDisabled,
AccountNoPermission, AccountNoPermission,

View File

@ -1,5 +1,6 @@
use axum::{extract::Request, middleware::Next, response::{IntoResponse, Response}}; use axum::{extract::Request, middleware::Next, response::{IntoResponse, Response}};
use http::{header, StatusCode}; use http::{header, StatusCode};
use i18n::{message, message_ids::MessageId};
use jsonwebtoken::{decode, DecodingKey, Validation}; use jsonwebtoken::{decode, DecodingKey, Validation};
use crate::{cache::account_cache::LOGIN_CACHE, config, context::{Context, WhiteContext}, token::Claims}; use crate::{cache::account_cache::LOGIN_CACHE, config, context::{Context, WhiteContext}, token::Claims};
@ -11,6 +12,7 @@ const WHITE_LIST: &[(&str, &str)] = &[
/// 认证中间件包括网络请求白名单、token验证、登录缓存 /// 认证中间件包括网络请求白名单、token验证、登录缓存
pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response { pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response {
let mut language = String::from("zh-CN");
// 获取请求的url和method然后判断是否在白名单中如果在白名单中则直接返回next(req),否则继续执行下面的代码 // 获取请求的url和method然后判断是否在白名单中如果在白名单中则直接返回next(req),否则继续执行下面的代码
let method = req.method().clone().to_string(); let method = req.method().clone().to_string();
let uri = req.uri().path_and_query().unwrap().to_string(); let uri = req.uri().path_and_query().unwrap().to_string();
@ -19,16 +21,16 @@ pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response {
}).is_some() { }).is_some() {
// 解析语言 // 解析语言
let language_header = req.headers().get(header::ACCEPT_LANGUAGE); let language_header = req.headers().get(header::ACCEPT_LANGUAGE);
let language = match language_header { language = match language_header {
Some(value) => { Some(value) => {
let value_str: Vec<&str> = value.to_str().unwrap_or("zh-CN").split(',').collect(); let value_str: Vec<&str> = value.to_str().unwrap_or("zh-CN").split(',').collect();
if value_str.is_empty() { if value_str.is_empty() {
String::from("zh-CN") language
} else { } else {
String::from(value_str[0]) String::from(value_str[0])
} }
}, },
None => String::from("zh-CN"), None => language,
}; };
req.extensions_mut().insert(WhiteContext { lang_tag: language }); req.extensions_mut().insert(WhiteContext { lang_tag: language });
return next.run(req).await; return next.run(req).await;
@ -40,11 +42,15 @@ pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response {
Some(header_value) => { Some(header_value) => {
let parts: Vec<&str> = header_value.to_str().unwrap_or("").split_whitespace().collect(); let parts: Vec<&str> = header_value.to_str().unwrap_or("").split_whitespace().collect();
if parts.len() != 2 || parts[0] != "Bearer" { if parts.len() != 2 || parts[0] != "Bearer" {
return (StatusCode::BAD_REQUEST, "Invalid authorization header format".to_string()).into_response(); tracing::error!("无效的 authorization 请求头参数");
return (StatusCode::BAD_REQUEST, message!(&language, MessageId::BadRequest)).into_response();
} }
parts[1] parts[1]
}, },
None => return (StatusCode::UNAUTHORIZED, "Missing authorization header".to_string()).into_response(), None => {
tracing::error!("缺少 authorization 请求头参数");
return (StatusCode::UNAUTHORIZED, message!(&language, MessageId::BadRequest)).into_response()
},
}; };
let validation = Validation::default(); let validation = Validation::default();
@ -53,7 +59,8 @@ pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response {
// 从缓存中获取当前用户信息 // 从缓存中获取当前用户信息
let account = LOGIN_CACHE.get(&decoded.claims.sub).await; let account = LOGIN_CACHE.get(&decoded.claims.sub).await;
if account.is_none() { if account.is_none() {
return (StatusCode::UNAUTHORIZED, "Invalid token".to_string()).into_response(); tracing::error!("无效的 token");
return (StatusCode::UNAUTHORIZED, message!(&language, MessageId::BadRequest)).into_response();
} }
let account = account.unwrap(); let account = account.unwrap();
// 判断token是否有效(注释掉,如果服务因为升级等原因手动重启了,缓存的数据也不再存在) // 判断token是否有效(注释掉,如果服务因为升级等原因手动重启了,缓存的数据也不再存在)
@ -69,6 +76,9 @@ pub async fn authenticate_ctx(mut req: Request, next: Next) -> Response {
}); });
next.run(req).await next.run(req).await
}, },
Err(_) => (StatusCode::UNAUTHORIZED, "Invalid token".to_string()).into_response(), Err(_) => {
tracing::error!("无效的 token");
return (StatusCode::UNAUTHORIZED, message!(&language, MessageId::BadRequest)).into_response();
}
} }
} }

View File

@ -5,6 +5,7 @@ use library::{context::{Context, WhiteContext}, model::{response::ResResult, val
use crate::service; use crate::service;
/// post: /account/google /// post: /account/google
///
/// google账号登录 /// google账号登录
pub async fn authenticate_google( pub async fn authenticate_google(
Extension(context): Extension<WhiteContext>, Extension(context): Extension<WhiteContext>,
@ -15,6 +16,7 @@ pub async fn authenticate_google(
} }
/// post: /account/sys /// post: /account/sys
///
/// 账号密码登录 /// 账号密码登录
pub async fn authenticate_with_password( pub async fn authenticate_with_password(
Extension(context): Extension<WhiteContext>, Extension(context): Extension<WhiteContext>,
@ -25,6 +27,7 @@ pub async fn authenticate_with_password(
} }
/// post: /account/refresh-token /// post: /account/refresh-token
///
/// 刷新token /// 刷新token
pub async fn refresh_token( pub async fn refresh_token(
Extension(context): Extension<Context>, Extension(context): Extension<Context>,

View File

@ -10,6 +10,7 @@ use library::model::validator;
use crate::service; use crate::service;
/// post: /feedback /// post: /feedback
///
/// 添加反馈信息 /// 添加反馈信息
pub async fn add_feedback( pub async fn add_feedback(
Extension(context): Extension<Context>, Extension(context): Extension<Context>,
@ -20,6 +21,7 @@ pub async fn add_feedback(
} }
/// get: /feedback /// get: /feedback
///
/// 获取反馈信息列表 /// 获取反馈信息列表
pub async fn get_feedback_list_by_page( pub async fn get_feedback_list_by_page(
Extension(context): Extension<Context>, Extension(context): Extension<Context>,