完善微信登陆信息校验

This commit is contained in:
liyunjia 2024-10-07 14:02:30 +08:00
parent 0d9647535f
commit b413898465
11 changed files with 84 additions and 16 deletions

1
Cargo.lock generated
View File

@ -1116,6 +1116,7 @@ dependencies = [
"chrono",
"domain",
"futures-util",
"hex",
"hex-literal",
"hmac",
"http",

View File

@ -56,4 +56,5 @@ hyper = "1.4.1"
tower = "0.4.13"
csv = "1.3.0"
strum = "0.26.3"
strum_macros = "0.26.3"
strum_macros = "0.26.3"
hex = "0.4.3"

View File

@ -2,10 +2,22 @@
use serde::{Deserialize, Serialize};
use validator::Validate;
/// 微信用户登录code通过wx.login获取
#[derive(Debug, Serialize, Deserialize, Validate)]
pub struct WxMinAppLogin {
/// 微信code
#[validate(required(message = "ValidateWxMinAppLoginCodeRequired"), length(min = 1, message = "ValidateWxMinAppLoginCodeRequired"))]
pub code: Option<String>,
}
/// 微信用户登陆后的信息
///
/// 通过code登录后获取openid、session_key、unionid
#[derive(Debug, Serialize, Deserialize, Validate)]
pub struct WxLoginInfo {
#[validate(required(message = "ValidateWxMinAppLoginInfoOpenidRequired"), length(min = 1, message = "ValidateWxMinAppLoginInfoOpenidRequired"))]
pub openid: Option<String>,
#[validate(required(message = "ValidateWxMinAppLoginSessionRequired"), length(min = 1, message = "ValidateWxMinAppLoginSessionRequired"))]
pub session_key: Option<String>,
pub unionid: Option<String>,
}

View File

@ -16,4 +16,6 @@ BadRequest,bad request,无效请求
InvalidParams,invalid params,无效参数
FailedGetWxAaccessToken,Failed to get WeChat access_token,获取微信access_token失败
FailedWeChatLogin,Failed to check WeChat login code,微信登录失败
ValidateWxMinAppLoginCodeRequired,login code is required,微信登陆code不能为空
ValidateWxMinAppLoginCodeRequired,login code is required,微信登陆code不能为空
ValidateWxMinAppLoginInfoOpenidRequired,openid is required,open id不能为空
ValidateWxMinAppLoginSessionRequired,session key is required,session key不能为空
1 id en-US zh-CN
16 InvalidParams invalid params 无效参数
17 FailedGetWxAaccessToken Failed to get WeChat access_token 获取微信access_token失败
18 FailedWeChatLogin Failed to check WeChat login code 微信登录失败
19 ValidateWxMinAppLoginCodeRequired login code is required 微信登陆code不能为空
20 ValidateWxMinAppLoginInfoOpenidRequired openid is required open id不能为空
21 ValidateWxMinAppLoginSessionRequired session key is required session key不能为空

View File

@ -42,4 +42,10 @@ pub enum MessageId {
FailedGetWxAaccessToken,
/// 微信登录失败校验code失败
FailedWeChatLogin,
/// 微信用户的openid不能为空
ValidateWxMinAppLoginInfoOpenidRequired,
/// 微信用户的session key不能为空
ValidateWxMinAppLoginSessionRequired,
}

View File

@ -35,6 +35,7 @@ tokio-cron-scheduler = { workspace = true }
tower-http = { workspace = true, features = ["trace"] }
tower = { workspace = true }
hyper = { workspace = true }
hex = { workspace = true }
domain = { path = "../domain" }
i18n = { path = "../i18n" }

View File

@ -12,6 +12,7 @@ const WHITE_LIST: &[(&str, &str)] = &[
("POST", "/account/google"),
("GET", "/wechat/access_token"),
("POST", "/wechat/code_2_session"),
("POST", "/wechat/check_session"),
];
/// 认证中间件包括网络请求白名单、token验证、登录缓存

View File

@ -164,28 +164,35 @@ impl WechatSocial {
/// https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/checkSessionKey.html
/// todo 使用的hmac签名是否正确签名结果转换成字符串是否正确目前未经验证
pub async fn check_session(&self, session_key: &str, open_id: &str) -> SocialResult<()> {
// let signature = Hmac::<Sha256>::new_from_slice(session.as_bytes()).expect("");
let token = self.get_access_token().await?;
let mut mac =
Hmac::<Sha256>::new_from_slice(session_key.as_bytes()).expect("初始化hmac失败");
mac.update(b"");
mac.update("".as_bytes());
let out = mac.finalize().into_bytes();
let signature = std::str::from_utf8(out.trim_ascii())
.unwrap_or_default()
.trim_ascii()
.to_string();
let response = reqwest::get(format!(
let signature = hex::encode(out);
let url = format!(
"https://api.weixin.qq.com/wxa/checksession?access_token={}&signature={}&openid={}&sig_method=hmac_sha256",
self.access_token.lock().await.access_token,
token.access_token,
signature,
open_id
))
);
let response = reqwest::get(url)
.await
.unwrap();
let result: WeChatBaseResult = response.json().await.unwrap();
let result: WeChatBaseResult = match response.json().await {
Ok(result) => result,
Err(err) => {
tracing::error!("微信登录信息校验失败,err:{:?}", err);
return Err(Box::new(ResErr::social(format!(
"微信登录信息校验失败,err:{}",
err
))));
}
};
if result.errcode != 0 {
tracing::error!("微信登录校验失败,errcode:{},errmsg:{}", result.errcode, result.errmsg);
tracing::error!("微信登录信息校验失败,errcode:{},errmsg:{}", result.errcode, result.errmsg);
return Err(Box::new(ResErr::social(format!(
"微信登录校验失败,errcode:{},errmsg:{}",
"微信登录信息校验失败,errcode:{},errmsg:{}",
result.errcode, result.errmsg
))));
}

View File

@ -18,4 +18,5 @@ pub fn init() -> Router {
// 微信相关路由
.typed_route(social_wx_controller::get_wechat_access_token)
.typed_route(social_wx_controller::code_2_session)
.typed_route(social_wx_controller::check_session)
}

View File

@ -1,18 +1,30 @@
use domain::dto::social_wx::WxMinAppLogin;
use domain::dto::social_wx::{WxMinAppLogin, WxLoginInfo};
use library::{context::Context, extractor::body_extractor::JsonBody, model::response::ResResult, social::wechat::{MiniAppLoginResult, WeChatAccessToken}};
use macros::{get, post};
use crate::service::social_wx_service;
/// 获取微信access_token
#[get("/wechat/access_token")]
pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccessToken> {
social_wx_service::get_wechat_access_token(context).await
}
/// 微信用户登录code通过wx.login获取
///
/// todo 登录成功后,判断用户是否存在,不存在,则注册用户,存在,则更新用户信息
#[post("/wechat/code_2_session")]
pub async fn code_2_session(
context: Context,
JsonBody(mini_app_login): JsonBody<WxMinAppLogin>
) -> ResResult<MiniAppLoginResult> {
social_wx_service::code_2_session(context, mini_app_login.code.unwrap()).await
}
/// 微信用户登录校验
#[post("/wechat/check_session")]
pub async fn check_session(
context: Context,
JsonBody(wx_login_info): JsonBody<WxLoginInfo>) -> ResResult<()> {
social_wx_service::check_session(context, wx_login_info).await
}

View File

@ -1,3 +1,4 @@
use domain::dto::social_wx::WxLoginInfo;
use i18n::{message, message_ids::MessageId};
use library::{
context::Context,
@ -5,6 +6,7 @@ use library::{
social::wechat::{MiniAppLoginResult, WeChatAccessToken, WECHAT_SOCIAL},
};
/// 获取微信access_token
pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccessToken> {
let lang_tag = context.get_lang_tag();
let access_token = match WECHAT_SOCIAL.get_access_token().await {
@ -20,6 +22,9 @@ pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccess
Ok(access_token)
}
/// 微信登录code通过wx.login获取
///
/// todo 登录成功后,判断用户是否存在,不存在,则注册用户,存在,则更新用户信息
pub async fn code_2_session(context: Context, code: String) -> ResResult<MiniAppLoginResult> {
let lang_tag = context.get_lang_tag();
let result = match WECHAT_SOCIAL.code_2_session(&code).await {
@ -34,3 +39,22 @@ pub async fn code_2_session(context: Context, code: String) -> ResResult<MiniApp
};
Ok(result)
}
/// 微信登录校验
///
/// todo 校验成功后,判断用户是否存在
pub async fn check_session(context: Context, wx_login_info: WxLoginInfo) -> ResResult<()> {
let lang_tag = context.get_lang_tag();
let session_key = wx_login_info.session_key.unwrap();
let open_id = wx_login_info.openid.unwrap();
match WECHAT_SOCIAL.check_session(&session_key, &open_id).await {
Ok(_) => Ok(()),
Err(err) => {
tracing::error!("微信登录校验失败,err:{}", err);
Err(ResErr::service(message!(
lang_tag,
MessageId::FailedWeChatLogin
)))
}
}
}