wechat_minapp/client/
access_token.rs

1use chrono::{DateTime, Duration, Utc};
2use serde::{Deserialize, Deserializer, Serialize};
3
4#[derive(Clone, Serialize)]
5pub struct AccessToken {
6    pub access_token: String,
7    pub expired_at: DateTime<Utc>,
8}
9
10impl std::fmt::Debug for AccessToken {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        f.debug_struct("AccessToken")
13            .field("access_token", &"********")
14            .field("expired_at", &self.expired_at)
15            .finish()
16    }
17}
18
19#[derive(Deserialize)]
20pub(crate) struct AccessTokenBuilder {
21    pub access_token: String,
22    #[serde(
23        deserialize_with = "AccessTokenBuilder::deserialize_expired_at",
24        rename = "expires_in"
25    )]
26    pub expired_at: DateTime<Utc>,
27}
28
29impl AccessTokenBuilder {
30    fn deserialize_expired_at<'de, D>(
31        deserializer: D,
32    ) -> std::result::Result<DateTime<Utc>, D::Error>
33    where
34        D: Deserializer<'de>,
35    {
36        let seconds = Duration::seconds(i64::deserialize(deserializer)?);
37
38        Ok(Utc::now() + seconds)
39    }
40}
41
42impl std::fmt::Debug for AccessTokenBuilder {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        f.debug_struct("AccessTokenBuilder")
45            .field("access_token", &"********")
46            .field("expired_at", &self.expired_at)
47            .finish()
48    }
49}
50
51impl AccessTokenBuilder {
52    pub fn build(self) -> AccessToken {
53        AccessToken {
54            access_token: self.access_token,
55            expired_at: self.expired_at,
56        }
57    }
58}
59
60/// 检查令牌是否过期
61///
62/// 添加安全边界,在令牌过期前5分钟就认为需要刷新
63pub(crate) fn is_token_expired(token: &AccessToken) -> bool {
64    // 添加安全边界,提前刷新
65    let now = Utc::now();
66    token.expired_at.signed_duration_since(now) < Duration::minutes(5)
67}