微信 MMTLS 協議詳解(五):加密實現

常用的解密算法,對稱非對稱 加密,密鑰協商, 帶消息認證的加解密
#生成RSA 密鑰對

void GenerateRsaKeypair(std::string& public_key,std::string& private_key)
{RSA* rsa = RSA_new();BIGNUM* bn = BN_new();// 生成 RSA 密鑰對BN_set_word(bn, RSA_F4);RSA_generate_key_ex(rsa, 2048, bn, nullptr);// 創建內存 BIO 用于存儲密鑰BIO* bio_private = BIO_new(BIO_s_mem());BIO* bio_public = BIO_new(BIO_s_mem());// 將私鑰寫入 BIO(PKCS#8 格式)PEM_write_bio_RSAPrivateKey(bio_private, rsa, nullptr, nullptr, 0, nullptr,nullptr);// 將公鑰寫入 BIO(X.509 格式)PEM_write_bio_RSAPublicKey(bio_public, rsa);// 從 BIO 提取字符串char* priv_data = nullptr;char* pub_data = nullptr;long priv_len = BIO_get_mem_data(bio_private, &priv_data);long pub_len = BIO_get_mem_data(bio_public, &pub_data);private_key = std::string(priv_data, priv_len);public_key = std::string(pub_data, pub_len);// 釋放資源BIO_free_all(bio_private);BIO_free_all(bio_public);RSA_free(rsa);BN_free(bn);
}

RSA 公鑰加密

// 公鑰加密
std::string EncryptWithPublic(const std::string& plaintext,const std::string& public_key) {BIO* bio = BIO_new_mem_buf(public_key.c_str(), -1);RSA* rsa = PEM_read_bio_RSAPublicKey(bio, nullptr, nullptr, nullptr);BIO_free_all(bio);if (!rsa) {return "";}int rsa_size = RSA_size(rsa);int max_plaintext_len =rsa_size - 42;  // RSA_PKCS1_OAEP_PADDING 填充后最大明文長度std::string encrypted_str;for (size_t i = 0; i < plaintext.length(); i += max_plaintext_len) {size_t len =std::min(max_plaintext_len, static_cast<int>(plaintext.length() - i));unsigned char* encrypted = new unsigned char[rsa_size];int result = RSA_public_encrypt(len, reinterpret_cast<const unsigned char*>(plaintext.c_str() + i),encrypted, rsa, RSA_PKCS1_OAEP_PADDING);if (result == -1) {RSA_free(rsa);delete[] encrypted;return "";}encrypted_str.append(reinterpret_cast<char*>(encrypted), result);delete[] encrypted;}RSA_free(rsa);return encrypted_str;
}

RSA 私鑰解密


// 私鑰解密
std::string DecryptWithPrivate(const std::string& ciphertext,const std::string& private_key) {BIO* bio = BIO_new_mem_buf(private_key.c_str(), -1);RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, nullptr);BIO_free_all(bio);if (!rsa) {return "";}int rsa_size = RSA_size(rsa);std::string decrypted_str;for (size_t i = 0; i < ciphertext.length(); i += rsa_size) {unsigned char* decrypted = new unsigned char[rsa_size];int result = RSA_private_decrypt(rsa_size,reinterpret_cast<const unsigned char*>(ciphertext.c_str() + i),decrypted, rsa, RSA_PKCS1_OAEP_PADDING);if (result == -1) {RSA_free(rsa);delete[] decrypted;return "";}decrypted_str.append(reinterpret_cast<char*>(decrypted), result);delete[] decrypted;}RSA_free(rsa);return decrypted_str;
}

生成ECDH 密鑰對

#include <openssl/digest.h>
#include <openssl/ecdh.h>
#include <openssl/ec_key.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/sha.h>
#include <openssl/cipher.h>
#include <openssl/bio.h>
#include <openssl/md5.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/aes.h>
bool GenECDHKeypair(int nid,std::string& public_key,std::string& private_key) {bool ret = false;EC_KEY* ec_key = nullptr;unsigned char* pub_key_buf = nullptr;unsigned char* pri_key_buf = nullptr;do {ec_key = EC_KEY_new_by_curve_name(nid);if (!ec_key) {break;}EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);ret = EC_KEY_generate_key(ec_key);if (ret != 1) {break;}int pub_key_size = i2o_ECPublicKey(ec_key, &pub_key_buf);if (pub_key_size == 0 || !pub_key_buf) {break;}int pri_key_size = i2d_ECPrivateKey(ec_key, &pri_key_buf);if (pri_key_size == 0 || !pri_key_buf) {break;}public_key.assign((const char*)pub_key_buf, pub_key_size);private_key.assign((const char*)pri_key_buf, pri_key_size);ret = true;} while (false);if (ec_key) {EC_KEY_free(ec_key);}if (pub_key_buf) {OPENSSL_free(pub_key_buf);}if (pri_key_buf) {OPENSSL_free(pri_key_buf);}return ret;
}

生成ECDSA 密鑰對


bool GenECDSAKeypair(int nid,std::string& public_key,std::string& private_key) {bool result = false;EC_KEY* ec_key = nullptr;BIO* bio = nullptr;do {ec_key = EC_KEY_new_by_curve_name(nid);if (!ec_key) {break;}EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);int ret = EC_KEY_generate_key(ec_key);if (ret != 1) {break;}ret = EC_KEY_check_key(ec_key);if (ret != 1) {break;}bio = BIO_new(BIO_s_mem());ret = PEM_write_bio_EC_PUBKEY(bio, ec_key);if (ret != 1 || BIO_flush(bio) != 1) {break;}char* ptr = nullptr;long size = BIO_get_mem_data(bio, &ptr);public_key.assign(ptr, size);BIO_free(bio);bio = BIO_new(BIO_s_mem());ret = PEM_write_bio_ECPrivateKey(bio, ec_key, nullptr, nullptr, 0, nullptr,nullptr);if (ret != 1 || BIO_flush(bio) != 1) {break;}ptr = nullptr;size = BIO_get_mem_data(bio, &ptr);private_key.assign(ptr, size);result = true;} while (false);if (nullptr != bio) {BIO_free(bio);}if (nullptr != ec_key) {EC_KEY_free(ec_key);}return result;
}

生成RSA 密鑰對


bool GenRsaKeypair(std::string& public_key, std::string& private_key)
{// 產生RSA密鑰RSA* rsa = RSA_new();BIGNUM* bn = BN_new();BN_set_word(bn, RSA_F4);RSA_generate_key_ex(rsa, 1024, bn, NULL);// 提取私鑰BIO* bio_private = BIO_new(BIO_s_mem());PEM_write_bio_RSAPrivateKey(bio_private, rsa, NULL, NULL, 0, NULL, NULL);int private_key_len = BIO_pending(bio_private);char* pem_private_key = (char*)calloc(private_key_len + 1, 1);BIO_read(bio_private, pem_private_key, private_key_len);private_key.assign(pem_private_key, private_key_len);free(pem_private_key);BIO_free(bio_private);// 提取公鑰BIO* bio_public = BIO_new(BIO_s_mem());PEM_write_bio_RSA_PUBKEY(bio_public, rsa);int public_key_len = BIO_pending(bio_public);char* pem_public_key = (char*)calloc(public_key_len + 1, 1);BIO_read(bio_public, pem_public_key, public_key_len);public_key.assign(pem_public_key, public_key_len);free(pem_public_key);BIO_free(bio_public);// 釋放資源RSA_free(rsa);BN_free(bn);return true;
}

SHA256 ECDH 密鑰協商

inline void* Sha256(const void* in,size_t in_len,void* out,size_t* out_len) {*out_len = SHA256_DIGEST_LENGTH;return SHA256((const uint8_t*)in, in_len, (uint8_t*)out);
}bool SHA256ECDH(int nid,const std::string& public_key,const std::string& private_key,std::string& result) {bool ret = false;EC_KEY* pub_ec_key = nullptr;EC_KEY* pri_ec_key = nullptr;do {pub_ec_key = EC_KEY_new_by_curve_name(nid);if (!pub_ec_key) {break;}auto uint8_pubkey = (const uint8_t*)public_key.data();pub_ec_key = o2i_ECPublicKey(&pub_ec_key, &uint8_pubkey, public_key.size());if (!pub_ec_key) {break;}pri_ec_key = EC_KEY_new_by_curve_name(nid);if (!pri_ec_key) {break;}auto uint8_privkey = (const uint8_t*)private_key.data();pri_ec_key = d2i_ECPrivateKey(&pri_ec_key, &uint8_privkey, private_key.size());if (!pri_ec_key) {break;}result.resize(SHA256_DIGEST_LENGTH);ECDH_compute_key(result.data(), SHA256_DIGEST_LENGTH,EC_KEY_get0_public_key(pub_ec_key), pri_ec_key, Sha256);ret = true;} while (false);// free memoryif (pub_ec_key) {EC_KEY_free(pub_ec_key);}if (pri_ec_key) {EC_KEY_free(pri_ec_key);}return ret;
}

MD5 ECDH 密鑰協商

inline void* MD5(const void* in,size_t in_len,void* out,size_t* out_len) {*out_len = MD5_DIGEST_LENGTH;MD5_CTX ctx;MD5_Init(&ctx);MD5_Update(&ctx, in, in_len);MD5_Final((uint8_t*)out, &ctx);return out;
}bool MD5ECDH(int nid,const std::string& public_key,const std::string& private_key,std::string& result) {bool ret = false;EC_KEY* pub_ec_key = nullptr;EC_KEY* pri_ec_key = nullptr;do {pub_ec_key = EC_KEY_new_by_curve_name(nid);if (!pub_ec_key) {break;}auto uint8_pubkey = (const uint8_t*)public_key.data();pub_ec_key = o2i_ECPublicKey(&pub_ec_key, &uint8_pubkey, public_key.size());if (!pub_ec_key) {break;}pri_ec_key = EC_KEY_new_by_curve_name(nid);if (!pri_ec_key) {break;}auto uint8_privkey = (const uint8_t*)private_key.data();pri_ec_key = d2i_ECPrivateKey(&pri_ec_key, &uint8_privkey, private_key.size());if (!pri_ec_key) {break;}result.resize(MD5_DIGEST_LENGTH);ECDH_compute_key(result.data(), MD5_DIGEST_LENGTH,EC_KEY_get0_public_key(pub_ec_key), pri_ec_key, MD5);   ret = true;} while (false);// free memoryif (pub_ec_key) {EC_KEY_free(pub_ec_key);}if (pri_ec_key) {EC_KEY_free(pri_ec_key);}return ret;
}

AES256GCM 加密


std::string Aes256GcmEncrypt(const void* once,int32_t once_len,const void* key,int32_t key_len,const void* aad,int32_t aad_len,const void* data,int32_t data_len,int tag_size,std::string& tag) {std::string result;EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();EVP_CIPHER_CTX_init(ctx);EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);do {// set iv sizeint ret =EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, once_len, nullptr);if (ret != 1) {break;}ret = EVP_EncryptInit_ex(ctx, nullptr, nullptr, (const uint8_t*)key,(const uint8_t*)once);if (ret != 1) {break;}int out_len = 0;if (aad_len != 0) {ret = EVP_EncryptUpdate(ctx, nullptr, &out_len,(const uint8_t*)aad, aad_len);if (ret != 1) {break;}}result.resize(EVP_CIPHER_CTX_block_size(ctx) + data_len);int encrypt_len = 0;if (data_len != 0) {ret = EVP_EncryptUpdate(ctx, (uint8_t*)result.data(), &out_len,(const uint8_t*)data, data_len);if (ret != 1) {break;}encrypt_len = out_len;}ret = EVP_EncryptFinal_ex(ctx, (uint8_t*)result.data() + encrypt_len,&out_len);if (ret != 1) {break;}encrypt_len += out_len;result.resize(encrypt_len);if (tag_size != 0) {tag.resize(tag_size);ret =EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, tag_size, tag.data());if (ret != 1) {break;}}} while (false);EVP_CIPHER_CTX_free(ctx);return result;
}

AES256 GCM 解密


std::string AesGcmDecrypt(const void* once,int32_t once_len,const void* key,int32_t key_len,const void* aad,int32_t aad_len,const void* tag,int32_t tag_len,const void* data,int32_t data_len) {std::string result;EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();EVP_CIPHER_CTX_init(ctx);EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr);do {int ret =EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, once_len, nullptr);if (ret != 1) {break;}ret = EVP_DecryptInit_ex(ctx, nullptr, nullptr, (const uint8_t*)key,(const uint8_t*)once);if (ret != 1) {break;}int out_len = 0;// set aadif (aad_len != 0) {ret = EVP_DecryptUpdate(ctx, nullptr, &out_len,(const uint8_t*)aad, aad_len);if (ret != 1) {break;}}result.resize(EVP_CIPHER_CTX_block_size(ctx) + data_len);int decrypt_len = 0;if (data_len != 0) {ret = EVP_DecryptUpdate(ctx, (uint8_t*)result.data(), &out_len,(const uint8_t*)data, data_len);if (ret != 1) {break;}decrypt_len = out_len;}ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, tag_len,(void*)tag);if (ret == 1) {ret = EVP_DecryptFinal_ex(ctx, (uint8_t*)result.data() + decrypt_len,&out_len);if (ret == 1) {decrypt_len += out_len;result.resize(decrypt_len);} else {result.clear();}} else {result.clear();}} while (false);EVP_CIPHER_CTX_free(ctx);return result;
}

AES 加密


std::string AESEncrypt(const std::string& key,const void* data,int32_t data_len) {if ( 0 == data_len){return "";}// 計算paddingint padding = 16 - data_len % 16;std::string padding_data(data_len + padding, (char)padding);memcpy(padding_data.data(), data, data_len);uint8_t tmp_key[16] = {};uint8_t* ptmp_key = nullptr;if (key.length() == 16) {ptmp_key = (uint8_t*)key.data();}else {ptmp_key = tmp_key;memcpy(tmp_key, key.data(), key.length() > 16 ? 16 : key.length());}uint8_t iv[16] = {};memcpy(iv, ptmp_key, 16);std::string result(padding_data.size(), 0);AES_KEY aes_key;AES_set_encrypt_key(ptmp_key, 128, &aes_key);AES_cbc_encrypt((uint8_t*)padding_data.data(), (uint8_t*)result.data(),padding_data.length(), &aes_key, iv, AES_ENCRYPT);return result;
}

AES 解密


std::string AESDecrypt(const std::string& key, const void* data, int32_t len)
{if (key.empty() || len == 0) {return "";}uint8_t tmp_key[16] = {};uint8_t* ptmp_key = nullptr;if (key.length() == 16){ptmp_key = (uint8_t*)key.data();}else{ptmp_key = tmp_key;memcpy(tmp_key, key.data(), key.length() > 16 ? 16 : key.length());}uint8_t iv[16] = {};memcpy(iv, ptmp_key, 16);std::string result(len, 0);AES_KEY aes_key;AES_set_decrypt_key(ptmp_key, 128, &aes_key);AES_cbc_encrypt((uint8_t*)data, (uint8_t*)result.data(), len,&aes_key, iv, AES_DECRYPT);// 去掉paddingchar padding = result[result.length() - 1];if (padding > 0 && padding <= 16){result.resize(result.length() - padding);}return result;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73624.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73624.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73624.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ROS melodic 安裝 python3 cv_bridge

有時候&#xff0c;我們需要處理這些兼容性問題。此處列舉我的過程&#xff0c;以供參考 mkdir -p my_ws_py39/src cd my_ws_py39 catkin_make_isolated-DPYTHON_EXECUTABLE/usr/bin/python3 \-DPYTHON_INCLUDE_DIR/usr/include/python3.8 \-DPYTHON_LIBRARY/usr/lib/x86_64-l…

深入學習:SpringQuartz的配置方式!

全文目錄&#xff1a; 開篇語前言摘要概述1. 基于 XML 的傳統配置配置步驟1.1 Maven 依賴1.2 XML 配置文件1.3 實現 Job 類 2. 基于 Java Config 的現代配置方式配置步驟2.1 Maven 依賴2.2 配置類2.3 實現 Job 類 3. 動態任務調度動態添加任務動態刪除任務 4. Quartz 持久化配置…

ClickHouse與TiDB實操對比:從入門到實戰的深度剖析

ClickHouse與TiDB實操對比&#xff1a;從入門到實戰的深度剖析 寶子們&#xff0c;在當今數據驅動的時代&#xff0c;選擇合適的數據庫對于處理海量數據和支撐業務發展至關重要。ClickHouse和TiDB作為兩款備受關注的數據庫&#xff0c;各自有著獨特的優勢和適用場景。今天&…

element-ui messageBox 組件源碼分享

messageBox 彈框組件源碼分享&#xff0c;主要從以下兩個方面&#xff1a; 1、messageBox 組件頁面結構。 2、messageBox 組件屬性。 一、組件頁面結構。 二、組件屬性。 2.1 title 標題&#xff0c;類型為 string&#xff0c;無默認值。 2.2 message 消息正文內容&#xf…

睡眠健康領域的智能硬件設備未來的發展趨勢

隨著社會節奏的不斷加快&#xff0c;人們的睡眠問題愈發多了起來&#xff0c;主要表現有以下幾個方面&#xff1a; 睡眠質量下降 淺睡眠增多&#xff1a;現代生活中&#xff0c;人們面臨著各種壓力源&#xff0c;如工作壓力、生活瑣事、經濟壓力等&#xff0c;這些壓力會導致大…

支付頁面安全與E-Skimming防護----淺談PCI DSS v4.0.1要求6.4.3與11.6.1的實施

關鍵詞&#xff1a;支付頁面安全、E-Skimming、PCI DSS v4.0.1、第三方腳本、風險管理、持卡人數據、數據安全、第三方服務提供商、TPSP、內容安全、網頁監控、惡意腳本攻擊 本文為atsec和作者技術共享類文章&#xff0c;旨在共同探討信息安全的相關話題。轉載請注明&#xff…

【gradio】從零搭建知識庫問答系統-Gradio+Ollama+Qwen2.5實現全流程

從零搭建大模型問答系統-GradioOllamaQwen2.5實現全流程&#xff08;一&#xff09; 前言一、界面設計&#xff08;計劃&#xff09;二、模塊設計1.登錄模塊2.注冊模塊3. 主界面模塊4. 歷史記錄模塊 三、相應的接口&#xff08;前后端交互&#xff09;四、實現前端界面的設計co…

案例分享|樹莓派媒體播放器,重構商場廣告的“黃金三秒”

研究顯示&#xff0c;與傳統戶外廣告相比&#xff0c;數字戶外廣告在消費者心中的記憶率提高了17%&#xff0c;而動態戶外廣告更是能提升16%的銷售業績&#xff0c;整體廣告效率提升了17%。這一顯著優勢&#xff0c;使得越來越多資源和技術流入數字廣告行業。 戶外裸眼3D廣告 無…

23種設計模式-裝飾器(Decorator)設計模式

裝飾器設計模式 &#x1f6a9;什么是裝飾器設計模式&#xff1f;&#x1f6a9;裝飾器設計模式的特點&#x1f6a9;裝飾器設計模式的結構&#x1f6a9;裝飾器設計模式的優缺點&#x1f6a9;裝飾器設計模式的Java實現&#x1f6a9;代碼總結&#x1f6a9;總結 &#x1f6a9;什么是…

[Vue]事件修飾符

文章目錄 一、語法介紹二、添加代碼三、結果展示四、參考文獻 如有錯誤&#xff0c;請指正&#xff01;&#xff01;&#xff01; 一、語法介紹 1、問題來源 我們在處理網頁時&#xff0c;當點擊按鈕時會觸發對應事件&#xff0c;但是有時并不想觸發該時間&#xff0c…

Go 語言 sync 包使用教程

Go 語言 sync 包使用教程 Go 語言的 sync 包提供了基本的同步原語&#xff0c;用于在并發編程中協調 goroutine 之間的操作。 1. 互斥鎖 (Mutex) 互斥鎖用于保護共享資源&#xff0c;確保同一時間只有一個 goroutine 可以訪問。 特點&#xff1a; 最基本的同步原語&#x…

ubuntu22.04安裝搜狗輸入法保姆教程~

一、添加中文語言支持 1.首先打開設置,找到Language and Region 2.點擊Manage Installed Languages 3.點擊 Install/Remove Languages... 4.選中Chinese (simplified),點擊Apply

docker中間件部署

1.docker安裝 # 1.卸載舊版本 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine# 2.需要的安裝包 yum install -y yum-utils# 3.設置鏡像的倉庫 # 3.1.默認是國外的&#x…

python康復日記-request庫的使用,爬蟲自動化測試

一&#xff0c;request的簡單應用 #1請求地址 URLhttps://example.com/login #2參數表單 form_data {username: admin,password: secret } #3返回的響應對象response response requests.post(URL,dataform_data,timeout5 ) #4處理返回結果&#xff0c;這里直接打印返回網頁的…

強化學習和智能決策:Q-Learning和Deep Q-Learning算法

強化學習(Reinforcement Learning, RL)是機器學習的一個重要分支,它通過智能體(Agent)與環境交互來學習最優決策策略,旨在最大化智能體的長期累積獎勵。Q-Learning和Deep Q-Learning是強化學習中的兩種關鍵算法,它們在智能決策領域發揮著重要作用。 一、強化學習基礎 …

ubuntu22.04 安裝Jitsi meet 開源會議系統,代替騰訊會議

0.安裝 官方安裝教程Self-Hosting Guide - Debian/Ubuntu server | Jitsi Meet 一定要用域名訪問&#xff0c; 一定要用域名訪問&#xff0c; 一定要用域名訪問&#xff0c; 一定要用域名訪問&#xff0c; 域名一定要有ssl證書&#xff0c;域名一定要有ssl證書&#xff0c;域名…

專家管理系統(源碼+文檔+講解+演示)

引言 在知識經濟時代&#xff0c;專家管理系統成為了企業優化知識資源、提升決策效率的重要工具。本文將介紹一款創新的專家管理系統&#xff0c;該系統通過智能化工具&#xff0c;助力企業實現專家資源的高效管理和利用。 平臺概述 專家管理系統采用前后端分離的架構設計&a…

css基礎-選擇器

選擇器進階 子串選擇器 /* 匹配 href 以 "https" 開頭的鏈接 */ a[href^"https"] {color: green; }/* 匹配 href 包含 "example" 的鏈接 */ a[href*"example"] {text-decoration: underline; }/* 匹配 href 以 ".pdf" 結尾…

Spring Boot屬性設置方法及優先級完整說明+表格對比

Spring Boot屬性設置方法及優先級完整說明 官網參考&#xff1a; https://docs.spring.io/spring-boot/3.4-SNAPSHOT/reference/features/external-config.html#features.external-config.files 屬性設置方法優先級順序&#xff08;從高到低&#xff09; 命令行參數&#xf…

上門家政小程序實戰,從0到1解決方案

一、邏輯分析 上門家政小程序主要涉及用戶端和服務端兩大部分。用戶端需要實現服務瀏覽、預約下單、訂單跟蹤等功能&#xff1b;服務端則要處理訂單管理、服務人員管理、數據統計等任務。以下是詳細的功能模塊分析&#xff1a; 用戶注冊與登錄&#xff1a;用戶通過手機號或第三…