本指南覆盖完整闭环:激活 → 本地保存 → 启动校验 → 停用。示例假设你已经拿到工作区与一份授权码。
依赖:reqwest(blocking 或 async)、serde_json、ed25519-dalek、base64、hex-literal。
1. 激活
src/license.rs
use serde_json::json;
const BASE: &str = "https://keygate.unionuser.com";
pub fn activate(license_key: &str, device_id: &str) -> Result<String, Box<dyn std::error::Error>> {
let client = reqwest::blocking::Client::new();
let res = client
.post(format!("{}/api/v1/license/activate", BASE))
.json(&json!({
"license_key": license_key,
"identifier": device_id,
"identifier_type": "device"
}))
.send()?;
let body: serde_json::Value = res.json()?;
if body["success"] != true {
return Err(body["error"]["code"].as_str().unwrap_or("UNKNOWN").into());
}
Ok(body["data"]["token"].as_str().unwrap_or_default().to_string())
}
2. 离线验签
src/verify.rs
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
use ed25519_dalek::{Signature, VerifyingKey};
use serde_json::Value;
const RAW_PUBLIC: [u8; 32] = hex_literal::hex!("bf17435ee7b937b3592808941683c21d37c4e124d95d4e8fdd86fe775dd23b11");
pub fn verify_token(token: &str, device_id: &str) -> Result<Value, Box<dyn std::error::Error>> {
let (payload_b64, sig_b64) = token.split_once('.').ok_or("BAD_TOKEN")?;
let sig_bytes = URL_SAFE_NO_PAD.decode(sig_b64)?;
let signature = Signature::from_slice(&sig_bytes)?;
let key = VerifyingKey::from_bytes(&RAW_PUBLIC)?;
key.verify_strict(payload_b64.as_bytes(), &signature)?;
let claims: Value = serde_json::from_slice(&URL_SAFE_NO_PAD.decode(payload_b64)?)?;
if claims["did"] != device_id {
return Err("DEVICE_MISMATCH".into());
}
Ok(claims)
}
3. 停用
src/license.rs
pub fn deactivate(license_key: &str, device_id: &str) -> Result<(), Box<dyn std::error::Error>> {
reqwest::blocking::Client::new()
.post(format!("{}/api/v1/license/deactivate", BASE))
.json(&json!({ "license_key": license_key, "identifier": device_id }))
.send()?;
Ok(())
}
Tauri 提示
- 把授权逻辑放在 Rust 侧(命令),前端只拿功能开关结果。
- token 存到 app_data_dir,不要放进前端 localStorage。
- ed25519-dalek 的 verify_strict 可拒绝弱公钥,建议使用。