fix: 调整路由顺序,修复静态文件被JWT拦截的问题

- 将 /static/{tail:.*} 路由移到受保护 scope 之前
- 确保静态文件无需认证即可访问
This commit is contained in:
2026-04-15 11:51:59 +08:00
parent 913a235e5c
commit a819a0db91
9 changed files with 728 additions and 678 deletions

125
src/handlers/auth.rs Normal file
View File

@@ -0,0 +1,125 @@
use actix_web::{web, HttpResponse, Responder, post};
use reqwest::Client;
use sqlx::postgres::PgPool;
use tracing::{debug, error, info, warn};
use crate::auth::generate_token;
use crate::models::{
AppState, ErrorResponse, TokenResponse, WeChatApiResponse, WeChatLoginRequest,
};
#[post("/api/login")]
pub async fn login(
pool: web::Data<PgPool>,
req: web::Json<WeChatLoginRequest>,
http_client: web::Data<Client>,
app_state: web::Data<AppState>,
) -> impl Responder {
let url = format!(
"https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code",
app_state.wechat_appid, app_state.wechat_secret, req.code
);
debug!("准备调用微信API: {}", url.replace(&app_state.wechat_secret, "***"));
let wechat_response = match http_client.get(&url).send().await {
Ok(response) => {
debug!("微信API请求成功状态码: {}", response.status());
response
}
Err(e) => {
error!("微信API请求失败: {}", e);
return HttpResponse::InternalServerError().json(ErrorResponse {
error: "请求微信服务失败".to_string(),
errcode: None,
errmsg: None,
});
}
};
let wechat_data: WeChatApiResponse = match wechat_response.json().await {
Ok(data) => {
debug!("微信API响应解析成功");
data
}
Err(e) => {
error!("微信API响应解析失败: {}", e);
return HttpResponse::InternalServerError().json(ErrorResponse {
error: "解析微信响应失败".to_string(),
errcode: None,
errmsg: None,
});
}
};
if let Some(errcode) = wechat_data.errcode {
warn!(
"微信登录验证失败 - 错误码: {}, 错误信息: {:?}",
errcode, wechat_data.errmsg
);
return HttpResponse::BadRequest().json(ErrorResponse {
error: "微信登录失败".to_string(),
errcode: Some(errcode),
errmsg: wechat_data.errmsg,
});
}
let openid = match wechat_data.openid {
Some(id) => {
let masked_openid = format!("{}***{}", &id[0..4], &id[id.len() - 4..]);
debug!("获取到用户openid: {}", masked_openid);
id
}
None => {
error!("微信API响应中未包含openid");
return HttpResponse::InternalServerError().json(ErrorResponse {
error: "微信API未返回openid".to_string(),
errcode: None,
errmsg: None,
});
}
};
let upsert_query = r#"
INSERT INTO users (openid, name, type)
VALUES ($1, left($1, 8), 2)
ON CONFLICT (openid) DO UPDATE SET id = users.id
RETURNING id
"#;
let user_id = match sqlx::query_as::<_, (i32,)>(upsert_query)
.bind(&openid)
.fetch_one(pool.get_ref())
.await
{
Ok((id,)) => {
info!("用户登录/注册成功用户ID: {}", id);
id
}
Err(e) => {
error!("用户信息处理失败: {}", e);
return HttpResponse::InternalServerError().json(ErrorResponse {
error: "用户信息处理失败".to_string(),
errcode: Some(500),
errmsg: None,
});
}
};
let token = match generate_token(user_id, &openid, 2, &app_state.jwt_secret) {
Ok(t) => {
debug!("JWT生成成功: {}", &t[0..8]);
t
}
Err(e) => {
error!("JWT生成失败: {}", e);
return HttpResponse::InternalServerError().json(ErrorResponse {
error: "生成身份令牌失败".to_string(),
errcode: Some(500),
errmsg: None,
});
}
};
let token_response = TokenResponse::new(token);
HttpResponse::Ok().json(token_response)
}