Rust版微信jssdk后端代码

字号+ 编辑: 国内TP粉 修订: 种花家 来源: 原创 2024-08-02 我要说两句(0)

又到了rust实战时间了,用rust实现jssdk后端获取access_token和jsapi_ticket

笔者要使用redis缓存一下请求到的凭据,所以需要在Cargo.toml里面引入一些必要的库

redis = { version = "0.26.1", features = ["tokio-comp"] }
reqwest = { version = "0.12.5" }
chrono = { version = "0.4.38" }
serde = { version = "1.0.204", features = ["derive"] }
serde_json = { version = "1.0.120" }
rand = { version = "0.8.5" }
sha1 = { version = "0.10.6" }
hex = { version = "0.4.3" }

笔者事先在项目文件里定义了几个常量,比如网站的域名、appid、appsecret

整个文件的源码

use reqwest::get;
use chrono::Utc;
use rand::Rng;
use redis::{AsyncCommands, RedisResult};
use serde::{Deserialize, Serialize};
use serde_json::{from_str, to_string};
use crate::conf::{DOMAIN_NAME, SOME_APPID, SOME_APPSECRET};
use crate::database::handler::redis;
use sha1::{Digest, Sha1};

#[derive(Serialize, Deserialize)]
struct JsApiTicket {
    expire_time: i64,
    jsapi_ticket: String,
}

#[derive(Serialize, Deserialize)]
struct AccessToken {
    expire_time: i64,
    access_token: String,
}

#[allow(dead_code)]
pub struct SignPackage {
    pub appid: String,
    pub nonce_str: String,
    pub timestamp: i64,
    pub url: String,
    pub signature: String,
    pub raw_string: String,
}

async fn sha1_encrypt(input: &str) -> String {
    let mut hasher = Sha1::new();
    hasher.update(input.as_bytes());
    let result = hasher.finalize();
    hex::encode(result)
}

pub async fn get_sign_package(request_uri: &str) -> SignPackage {
    let jsapiticket = get_jsapiticket().await;

    let url: &str = &format!("https://{}{}", DOMAIN_NAME, request_uri);

    let timestamp: i64 = Utc::now().timestamp();
    let nonce_str = create_nonce_str(16).await;

    let raw_string: &str = &format!("jsapi_ticket={}&noncestr={}&timestamp={}&url={}", jsapiticket, nonce_str, timestamp, url);

    let signature = sha1_encrypt(raw_string).await;

    return SignPackage {
        appid: SOME_APPID.to_string(),
        nonce_str,
        timestamp,
        url: url.to_string(),
        signature,
        raw_string: raw_string.to_string(),
    };
}

async fn create_nonce_str(length: usize) -> String {
    let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    let mut rng = rand::thread_rng();
    let mut str = String::new();

    for _ in 0..length {
        str.push(chars.chars().nth(rng.gen_range(0..chars.len())).unwrap());
    }

    str
}

pub async fn get_jsapiticket() -> String {
    let mut conn = redis(1).await;

    let data_r: RedisResult<String> = conn.get("JsapiTicket").await;
    // let data_r = redis::cmd("GET").arg("JsapiTicket").query_async(&mut conn).await;

 let ticket: JsApiTicket;
    if let Err(_) = &data_r {
        tracing::warn!("Redis GET Jsapi ticket failed.");
        ticket = JsApiTicket {
            expire_time: 0,
            jsapi_ticket: "".to_string(),
        }
    } else {
        let ticket_json = data_r.unwrap();
        ticket = from_str(&ticket_json).unwrap();
    }

    let now: i64 = Utc::now().timestamp();

    if ticket.expire_time == 0 || ticket.expire_time < now {
        let token = get_access_token().await;

        if "" == &token {
            return "".to_string();
        }

        let resp = get(&format!("https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&access_token={}", &token)).await;
        if let Err(e) = &resp {
            tracing::warn!("Request access token failed: {:?}", e);
            return "".to_string();
        } else {
            let ticket_json = resp.unwrap().text().await.unwrap();
            let ticket_r = from_str(&ticket_json);

            let ticket = ticket_r.unwrap_or(JsApiTicket {
                expire_time: 0,
                jsapi_ticket: "".to_string(),
            });

            if ticket.expire_time != 0 {
                let tmp_ticket = JsApiTicket {
                    expire_time: now + 7000,
                    jsapi_ticket: ticket.jsapi_ticket.clone(),
                };

                let ticket_json = to_string(&tmp_ticket).unwrap();

                let r1: RedisResult<()> = conn.set("JsapiTicket", &ticket_json).await;
                if let Err(_) = r1 {
                    tracing::warn!("Redis SET JsApi Ticket Failed.");
                } else {
                    let _: RedisResult<()> = conn.expire("JsapiTicket", 7000).await;
                }

                // redis::cmd("SET").arg(&[]).query(&mut conn).await;
    // redis::cmd("EXPIRE").arg(&["JsapiTicket"]).arg(7000).query(&mut conn).await;

    return ticket.jsapi_ticket;
            }
        }
    }

    return ticket.jsapi_ticket;
}

pub async fn get_access_token() -> String {
    let mut conn = redis(1).await;

    let data_r: RedisResult<String> = redis::cmd("GET").arg("AccessToken").query_async(&mut conn).await;

    let token: AccessToken;
    if let Err(_) = &data_r {
        tracing::warn!("Redis GET access token failed.");
        token = AccessToken {
            expire_time: 0,
            access_token: "".to_string(),
        }
    } else {
        let token_json = data_r.unwrap();
        token = from_str(&token_json).unwrap();
    }

    let now: i64 = Utc::now().timestamp();

    if token.expire_time == 0 || token.expire_time < now {
        let resp = get(&format!("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}", SOME_APPID, SOME_APPSECRET)).await;

        if let Err(_) = &resp {
            return "".to_string();
        } else {
            let token_json = resp.unwrap().text().await.unwrap();
            let token_r = from_str(&token_json);

            let token = token_r.unwrap_or(AccessToken {
                expire_time: 0,
                access_token: "".to_string(),
            });

            if token.expire_time != 0 {
                let tmp_token = AccessToken {
                    expire_time: now + 7000,
                    access_token: token.access_token.clone(),
                };

                let token_json = to_string(&tmp_token).unwrap();

                let r1: RedisResult<()> = redis::cmd("SET").arg(&token_json).query_async(&mut conn).await;
                if let Err(_) = r1 {
                    tracing::warn!("Redis SET access token Failed.");
                } else {
                    let _: RedisResult<()> = redis::cmd("EXPIRE").arg(&token_json).arg(7000).query_async(&mut conn).await;
                }

                return token.access_token;
            }
        }

        return "".to_string();
    } else {
        return token.access_token;
    }
}


阅完此文,您的感想如何?
  • 有用

    1

  • 没用

    0

  • 开心

    0

  • 愤怒

    0

  • 可怜

    0

1.如文章侵犯了您的版权,请发邮件通知本站,该文章将在24小时内删除;
2.本站标注原创的文章,转发时烦请注明来源;
3.Q群: 2702237 13835667

相关课文
  • 在rust/axum框架中操作redis

  • rust编译新的wasm项目操作流程(原文: 编译 Rust 为 WebAssembly)

  • rust视图模板库askama的使用

  • axum框架当中获取请求header, 和获取header指定字段的方法

我要说说
网上嘉宾点评