完善微信登陆信息校验
This commit is contained in:
parent
0d9647535f
commit
b413898465
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1116,6 +1116,7 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"domain",
|
"domain",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"hex",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
"hmac",
|
"hmac",
|
||||||
"http",
|
"http",
|
||||||
|
@ -56,4 +56,5 @@ hyper = "1.4.1"
|
|||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
csv = "1.3.0"
|
csv = "1.3.0"
|
||||||
strum = "0.26.3"
|
strum = "0.26.3"
|
||||||
strum_macros = "0.26.3"
|
strum_macros = "0.26.3"
|
||||||
|
hex = "0.4.3"
|
@ -2,10 +2,22 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
|
/// 微信用户登录,code通过wx.login获取
|
||||||
#[derive(Debug, Serialize, Deserialize, Validate)]
|
#[derive(Debug, Serialize, Deserialize, Validate)]
|
||||||
pub struct WxMinAppLogin {
|
pub struct WxMinAppLogin {
|
||||||
/// 微信code
|
/// 微信code
|
||||||
#[validate(required(message = "ValidateWxMinAppLoginCodeRequired"), length(min = 1, message = "ValidateWxMinAppLoginCodeRequired"))]
|
#[validate(required(message = "ValidateWxMinAppLoginCodeRequired"), length(min = 1, message = "ValidateWxMinAppLoginCodeRequired"))]
|
||||||
pub code: Option<String>,
|
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>,
|
||||||
}
|
}
|
4
i18n.csv
4
i18n.csv
@ -16,4 +16,6 @@ BadRequest,bad request,无效请求
|
|||||||
InvalidParams,invalid params,无效参数
|
InvalidParams,invalid params,无效参数
|
||||||
FailedGetWxAaccessToken,Failed to get WeChat access_token,获取微信access_token失败
|
FailedGetWxAaccessToken,Failed to get WeChat access_token,获取微信access_token失败
|
||||||
FailedWeChatLogin,Failed to check WeChat login code,微信登录失败
|
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不能为空
|
|
@ -42,4 +42,10 @@ pub enum MessageId {
|
|||||||
FailedGetWxAaccessToken,
|
FailedGetWxAaccessToken,
|
||||||
/// 微信登录失败,校验code失败
|
/// 微信登录失败,校验code失败
|
||||||
FailedWeChatLogin,
|
FailedWeChatLogin,
|
||||||
|
/// 微信用户的openid不能为空
|
||||||
|
ValidateWxMinAppLoginInfoOpenidRequired,
|
||||||
|
/// 微信用户的session key不能为空
|
||||||
|
ValidateWxMinAppLoginSessionRequired,
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ tokio-cron-scheduler = { workspace = true }
|
|||||||
tower-http = { workspace = true, features = ["trace"] }
|
tower-http = { workspace = true, features = ["trace"] }
|
||||||
tower = { workspace = true }
|
tower = { workspace = true }
|
||||||
hyper = { workspace = true }
|
hyper = { workspace = true }
|
||||||
|
hex = { workspace = true }
|
||||||
|
|
||||||
domain = { path = "../domain" }
|
domain = { path = "../domain" }
|
||||||
i18n = { path = "../i18n" }
|
i18n = { path = "../i18n" }
|
||||||
|
@ -12,6 +12,7 @@ const WHITE_LIST: &[(&str, &str)] = &[
|
|||||||
("POST", "/account/google"),
|
("POST", "/account/google"),
|
||||||
("GET", "/wechat/access_token"),
|
("GET", "/wechat/access_token"),
|
||||||
("POST", "/wechat/code_2_session"),
|
("POST", "/wechat/code_2_session"),
|
||||||
|
("POST", "/wechat/check_session"),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// 认证中间件,包括网络请求白名单、token验证、登录缓存
|
/// 认证中间件,包括网络请求白名单、token验证、登录缓存
|
||||||
|
@ -164,28 +164,35 @@ impl WechatSocial {
|
|||||||
/// https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/checkSessionKey.html
|
/// https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/checkSessionKey.html
|
||||||
/// todo 使用的hmac签名是否正确,签名结果转换成字符串是否正确,目前未经验证
|
/// todo 使用的hmac签名是否正确,签名结果转换成字符串是否正确,目前未经验证
|
||||||
pub async fn check_session(&self, session_key: &str, open_id: &str) -> SocialResult<()> {
|
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 =
|
let mut mac =
|
||||||
Hmac::<Sha256>::new_from_slice(session_key.as_bytes()).expect("初始化hmac失败");
|
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 out = mac.finalize().into_bytes();
|
||||||
let signature = std::str::from_utf8(out.trim_ascii())
|
let signature = hex::encode(out);
|
||||||
.unwrap_or_default()
|
let url = format!(
|
||||||
.trim_ascii()
|
|
||||||
.to_string();
|
|
||||||
let response = reqwest::get(format!(
|
|
||||||
"https://api.weixin.qq.com/wxa/checksession?access_token={}&signature={}&openid={}&sig_method=hmac_sha256",
|
"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,
|
signature,
|
||||||
open_id
|
open_id
|
||||||
))
|
);
|
||||||
|
let response = reqwest::get(url)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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 {
|
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!(
|
return Err(Box::new(ResErr::social(format!(
|
||||||
"微信登录校验失败,errcode:{},errmsg:{}",
|
"微信登录信息校验失败,errcode:{},errmsg:{}",
|
||||||
result.errcode, result.errmsg
|
result.errcode, result.errmsg
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,5 @@ pub fn init() -> Router {
|
|||||||
// 微信相关路由
|
// 微信相关路由
|
||||||
.typed_route(social_wx_controller::get_wechat_access_token)
|
.typed_route(social_wx_controller::get_wechat_access_token)
|
||||||
.typed_route(social_wx_controller::code_2_session)
|
.typed_route(social_wx_controller::code_2_session)
|
||||||
|
.typed_route(social_wx_controller::check_session)
|
||||||
}
|
}
|
||||||
|
@ -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 library::{context::Context, extractor::body_extractor::JsonBody, model::response::ResResult, social::wechat::{MiniAppLoginResult, WeChatAccessToken}};
|
||||||
use macros::{get, post};
|
use macros::{get, post};
|
||||||
|
|
||||||
use crate::service::social_wx_service;
|
use crate::service::social_wx_service;
|
||||||
|
|
||||||
|
/// 获取微信access_token
|
||||||
#[get("/wechat/access_token")]
|
#[get("/wechat/access_token")]
|
||||||
pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccessToken> {
|
pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccessToken> {
|
||||||
social_wx_service::get_wechat_access_token(context).await
|
social_wx_service::get_wechat_access_token(context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 微信用户登录,code通过wx.login获取
|
||||||
|
///
|
||||||
|
/// todo 登录成功后,判断用户是否存在,不存在,则注册用户,存在,则更新用户信息
|
||||||
#[post("/wechat/code_2_session")]
|
#[post("/wechat/code_2_session")]
|
||||||
pub async fn code_2_session(
|
pub async fn code_2_session(
|
||||||
context: Context,
|
context: Context,
|
||||||
JsonBody(mini_app_login): JsonBody<WxMinAppLogin>
|
JsonBody(mini_app_login): JsonBody<WxMinAppLogin>
|
||||||
) -> ResResult<MiniAppLoginResult> {
|
) -> ResResult<MiniAppLoginResult> {
|
||||||
social_wx_service::code_2_session(context, mini_app_login.code.unwrap()).await
|
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
|
||||||
}
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
use domain::dto::social_wx::WxLoginInfo;
|
||||||
use i18n::{message, message_ids::MessageId};
|
use i18n::{message, message_ids::MessageId};
|
||||||
use library::{
|
use library::{
|
||||||
context::Context,
|
context::Context,
|
||||||
@ -5,6 +6,7 @@ use library::{
|
|||||||
social::wechat::{MiniAppLoginResult, WeChatAccessToken, WECHAT_SOCIAL},
|
social::wechat::{MiniAppLoginResult, WeChatAccessToken, WECHAT_SOCIAL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// 获取微信access_token
|
||||||
pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccessToken> {
|
pub async fn get_wechat_access_token(context: Context) -> ResResult<WeChatAccessToken> {
|
||||||
let lang_tag = context.get_lang_tag();
|
let lang_tag = context.get_lang_tag();
|
||||||
let access_token = match WECHAT_SOCIAL.get_access_token().await {
|
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)
|
Ok(access_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 微信登录,code通过wx.login获取
|
||||||
|
///
|
||||||
|
/// todo 登录成功后,判断用户是否存在,不存在,则注册用户,存在,则更新用户信息
|
||||||
pub async fn code_2_session(context: Context, code: String) -> ResResult<MiniAppLoginResult> {
|
pub async fn code_2_session(context: Context, code: String) -> ResResult<MiniAppLoginResult> {
|
||||||
let lang_tag = context.get_lang_tag();
|
let lang_tag = context.get_lang_tag();
|
||||||
let result = match WECHAT_SOCIAL.code_2_session(&code).await {
|
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)
|
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
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user