iOS-SM3加密算法N種集成

近期的一個項目需要用到SM3加密算法,需要在iOS中使用Objective-C實現SM3國密加密算法。

SM3:是中國國家密碼管理局發布的密碼雜湊算法標準,適用于商用密碼應用中的數字簽名和驗證、消息認證碼的生成與驗證以及隨機數的生成等

由于iOS系統并未內置SM3算法,我們需要使用第三方開源庫或自己實現

GMObjC庫: 是一個基于 OpenSSL 的國密(SM2、SM3、SM4)算法的 Objective-C 開源庫,適用于 iOS 和 macOS 開發。它封裝了中國國家密碼管理局發布的多種加密算法,包括:

1. SM2: 支持基于橢圓曲線(ECC)的加解密,密鑰協商(ECDH)和簽名算法

2. SM3: 類似 SHA 系列的國密哈希算法,包含 SM3 和 HMAC 等

3. SM4: 實現對稱分組加密算法

**GmSSL庫:**GmSSL是由北京大學自主開發的國產商用密碼開源庫,實現了對國密算法、標準和安全通信協議的全面功能覆蓋,支持包括移動端在內的主流操作系統和處理器,支持密碼鑰匙、密碼卡等典型國產密碼硬件,提供功能豐富的命令行工具及多種編譯語言編程接口

方案一:使用第三方庫(GMObjC)

集成GMObjC:集成GMObjC方法

因為我們的項目是SDK不便用CocoaPods方法,因此我只能選擇直接集成和手動編譯為 Framework。

1.直接集成 (demo)

1.從 Git 下載最新代碼,找到和 README 同級的 GMObjC 文件夾,將 GMObjC 文件夾拖入項目

2.找到和 README 同級的 Frameworks 文件夾,將項目 Frameworks/OpenSSL.xcframework 拖入項目

3.在需要使用的地方導入頭文件 GMObjC.h 即可使用 SM2、SM4 加解密,簽名驗簽,計算 SM3 摘要等

注意事項

GMObjC 依賴 OpenSSL,可直接拖入 Frameworks/OpenSSL.xcframework 或通過pod GMOpenSSL安裝 OpenSSL。

如果項目中已集成 OpenSSL 1.1.1l 以上版本,可共用同一個 OpenSSL;否則需要使用 Carthage 將 GMObjC 編譯為動態庫。

我按照以上步驟將文件導入后報錯:
OpenSSL.xcframework 簽名驗證失敗

OpenSSL.xcframework報錯

終端執行強制重簽名命令

codesign --force --deep --sign - 你的路徑/OpenSSL.xcframework

返回:你的路徑/OpenSSL.xcframework: replacing existing signature

現在就可以運行測試了:

#import "GMObjC.h"NSString *str = @"123@1234";
NSString *digest = [GMSm3Utils hashWithText:str];
NSLog(@"%@", digest);

2.手動編譯為 Framework (demo)

1.動態庫?:

從 GitHub 下載源碼,打開項目GMObjC-master/Frameworks/GMObjC.xcframework把這個拖入項目

在 Xcode 的 General → Frameworks, Libraries, and Embedded Content 中需標記為 Embed & Sign

Embed & Sign

#import "GMObjC/GMObjC.h"NSString *digest1 = [GMSm3Utils hashWithText:str];
NSLog(@"%@", digest1);

2.靜態庫:

從 GitHub 下載源碼,打開項目 GMObjC.xcodeproj,設置 Build Settings - Linking-General - Mach-O Type 為 Static Library

手動編譯為靜態庫 GMObjC.framework

**合并為 XCFramework:**通過xcodebuild -create-xcframework命令來合并為 XCFramework,通過合并 GMObjC 庫的模擬器和真機版本來演示

# 創建合并包 GMObjC.xcframeworkxcodebuild -create-xcframework \-framework Release-iphoneos/GMObjC.framework \-framework Release-iphonesimulator/GMObjC.framework \-output GMObjC.xcframework

把生成的GMObjC.xcframework拖入項目即可

3.CocoaPods安裝GMObjC (GMObjC-demo) (GMDynamic-demo)

GMObjC 和 GMDynamic 只能安裝其中一個,二者不能同時安裝。

GMObjC 為靜態庫,GMDynamic 為編譯好的 GMObjC 動態庫版本。

# 安裝 GMObjC 的源碼和 GMOpenSSL.xcframework (靜態庫)
pod 'GMObjC', '~> 4.0.3'# 當 Podfile 中使用 use_frameworks! 時,安裝 GMObjC.xcframework (動態庫)
pod 'GMDynamic', '~> 4.0.3'

方案二:使用第三方庫(GmSSL)(demo)

集成GmSSL:

集成GmSSL方法

但是我用這種方法不行,我用了其他的方法。

我們使用GmSSL 3.x(master分支)來編譯iOS的靜態庫(libcrypto.a和libssl.a)。由于3.x版本采用了CMake構建系統,因此流程與2.x不同。

GmSSL 3.x 的構建系統已經發生了變化,生成的庫文件名為 libgmssl.a 而不是傳統的 libcrypto.a 和 libssl.a。

如果項目必須使用 libcrypto.alibssl.a,請回退到 GmSSL 2.x

  1. 克隆代碼并切換到master分支(或最新的穩定標簽)

  2. 配置CMake工具鏈文件(為iOS交叉編譯)

  3. 分別編譯arm64(真機)和x86_64(模擬器)架構

  4. 使用lipo合并成通用靜態庫

  5. 將生成的靜態庫和頭文件集成到iOS項目中。

創建編譯腳本: build_ios.sh(放在GmSSL根目錄)

#!/bin/bash
set -e# 確保使用正確的路徑
export PATH="/usr/local/bin:$PATH"# 設置環境變量
export XCODE_PATH=$(xcode-select -p)
export IOS_SDK=$XCODE_PATH/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
export SIM_SDK=$XCODE_PATH/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk# 創建輸出目錄
OUTPUT_DIR="build-ios"
rm -rf $OUTPUT_DIR
mkdir -p $OUTPUT_DIR# 編譯函數
compile_arch() {ARCH=$1SDK=$2BUILD_DIR="${OUTPUT_DIR}/${ARCH}"mkdir -p $BUILD_DIRpushd $BUILD_DIR > /dev/nullecho "? 配置 $ARCH..."cmake ../.. \-DCMAKE_SYSTEM_NAME=iOS \-DCMAKE_OSX_ARCHITECTURES=$ARCH \-DCMAKE_OSX_SYSROOT=$SDK \-DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \-DCMAKE_BUILD_TYPE=Release \-DBUILD_SHARED_LIBS=OFF \-DENABLE_SM2=ON \-DENABLE_SM3=ON \-DENABLE_SM4=ON \-DENABLE_SM9=ON \-G Ninjaecho "? 編譯 $ARCH..."ninja# 關鍵修改:GmSSL 3.x 生成的庫是 libgmssl.amkdir -p libcp bin/libgmssl.a lib/popd > /dev/null
}# 編譯各架構
compile_arch "arm64" "$IOS_SDK"
compile_arch "x86_64" "$SIM_SDK"# 合并通用庫
UNIVERSAL_DIR="${OUTPUT_DIR}/universal"
mkdir -p $UNIVERSAL_DIR/lib# 合并為單個庫 (GmSSL 3.x 只生成一個庫)
lipo -create \"${OUTPUT_DIR}/arm64/lib/libgmssl.a" \"${OUTPUT_DIR}/x86_64/lib/libgmssl.a" \-output "$UNIVERSAL_DIR/lib/libgmssl.a"# 復制頭文件
if [ -d "${OUTPUT_DIR}/arm64/include" ]; thencp -R "${OUTPUT_DIR}/arm64/include" "$UNIVERSAL_DIR/"
elif [ -d "../../include" ]; thencp -R "../../include" "$UNIVERSAL_DIR/"
elseecho "?? 警告: 找不到頭文件目錄"
fiecho "? 編譯成功!"
echo "庫文件位置: $UNIVERSAL_DIR/lib/libgmssl.a"
echo "頭文件位置: $UNIVERSAL_DIR/include"# 驗證文件
file "$UNIVERSAL_DIR/lib"/*.a
lipo -info "$UNIVERSAL_DIR/lib/libgmssl.a"

然后按照以下步驟進行執行:

# 安裝構建工具
brew install cmake ninja pkg-config# 獲取最新代碼
git clone https://github.com/guanzhi/GmSSL.git
cd GmSSL
git checkout master  # 確保使用最新版本
git pull# 2. 執行編譯
chmod +x build_ios.sh
./build_ios.sh
  1. 將GmSSL/build-ios/universal/lib/libgmssl.a 拖入項目

  2. 將GmSSL/include/gmssl 拖入項目

  3. import “sm3.h”

封裝方法:

@interface GmSSLEncryptorSM3 : NSObject+ (NSString *)sm3HashWithString:(NSString *)input;
+ (NSData *)sm3HashWithData:(NSData *)data;@end@implementation GmSSLEncryptorSM3+ (instancetype)encryptor {return [[GmSSLEncryptorSM3 alloc] init];
}+ (NSData *)sm3HashWithData:(NSData *)data {// 初始化 SM3 上下文SM3_CTX ctx;sm3_init(&ctx);// 添加數據到哈希計算sm3_update(&ctx, data.bytes, data.length);// 準備存儲結果的緩沖區 (SM3 輸出為 32 字節)uint8_t dgst[SM3_DIGEST_SIZE];// 完成哈希計算sm3_finish(&ctx, dgst);// 轉換為 NSDatareturn [NSData dataWithBytes:dgst length:SM3_DIGEST_SIZE];
}+ (NSString *)sm3HashWithString:(NSString *)input {NSData *inputData = [input dataUsingEncoding:NSUTF8StringEncoding];// 計算 SM3 哈希NSData *hashData = [GmSSLEncryptorSM3 sm3HashWithData:inputData];// 轉換為十六進制字符串顯示NSMutableString *hexString = [NSMutableString string];const uint8_t *bytes = (const uint8_t *)hashData.bytes;for (NSUInteger i = 0; i < hashData.length; i++) {[hexString appendFormat:@"%02x", bytes[i]];}return hexString;
}
@end

就可以在項目中使用了:

NSString *encryptor = [GmSSLEncryptorSM3 sm3HashWithString:str];
NSLog(@"%@", encryptor);

方案三:純 Objective-C 實現(無依賴)(demo)

SM3本質上不是加密算法,它是是一種雜湊函數,是在[SHA-256]基礎上改進實現的一種算法,它不是對數據進行加密然后再解密,而是生成一個256位的散列值,因此SM3適用于內容摘要,數字簽名驗證或密碼驗證等。

SM3算法的執行過程:

根據SM3標準文檔(GM/T 0004-2012)

sm3流程.png

**消息擴展:**將16個32位字擴展為68個字(W)和64個字(W1),使用P1宏。
**壓縮函數:**64輪迭代更新寄存器(A-H),每輪使用FF1/GG1等宏。
**常量:**壓縮函數中的常量0x7A879D8A(TJ的固定值)。
**結果輸出:**將最終狀態寄存器轉換為大端序字節流(256位)。

//
//  SM3Encryptor.m
//  testDemo
//
//  Created by wt on 2025/6/12.
//#import "SM3Encryptor.h"
#include <stdint.h>// SM3 上下文結構
typedef struct {uint32_t state[8];   // 8個32位寄存器(A-H)uint64_t totalLength; // 總消息長度(位)uint8_t buffer[64];  // 當前數據塊緩存uint32_t bufferLength; // 當前緩沖區長度
} SM3Context;// 循環左移
static inline uint32_t ROTL(uint32_t x, uint8_t n) {return (x << n) | (x >> (32 - n));
}// 布爾函數 FF0(0≤j≤15)
static inline uint32_t FF0(uint32_t x, uint32_t y, uint32_t z) {return x ^ y ^ z;
}// 布爾函數 FF1(16≤j≤63)
static inline uint32_t FF1(uint32_t x, uint32_t y, uint32_t z) {return (x & y) | (x & z) | (y & z);
}// 布爾函數 GG0(0≤j≤15)
static inline uint32_t GG0(uint32_t x, uint32_t y, uint32_t z) {return x ^ y ^ z;
}// 布爾函數 GG1(16≤j≤63)
static inline uint32_t GG1(uint32_t x, uint32_t y, uint32_t z) {return (x & y) | ((~x) & z);
}// 置換函數 P0
static inline uint32_t P0(uint32_t x) {return x ^ ROTL(x, 9) ^ ROTL(x, 17);
}// 置換函數 P1
static inline uint32_t P1(uint32_t x) {return x ^ ROTL(x, 15) ^ ROTL(x, 23);
}// 初始化SM3上下文
void SM3Init(SM3Context *context) {// SM3標準初始值context->state[0] = 0x7380166F;context->state[1] = 0x4914B2B9;context->state[2] = 0x172442D7;context->state[3] = 0xDA8A0600;context->state[4] = 0xA96F30BC;context->state[5] = 0x163138AA;context->state[6] = 0xE38DEE4D;context->state[7] = 0xB0FB0E4E;context->totalLength = 0;context->bufferLength = 0;memset(context->buffer, 0, 64);
}// 處理單個64字節塊(壓縮函數核心)
void SM3Compress(SM3Context *context, const uint8_t block[64]) {// 1. 消息擴展:16字 → 68字(W) + 64字(W1)uint32_t W[68], W1[64];// 初始化前16字(大端序轉換)for (int i = 0; i < 16; i++) {W[i] = (uint32_t)block[i*4] << 24 |(uint32_t)block[i*4+1] << 16 |(uint32_t)block[i*4+2] << 8 |(uint32_t)block[i*4+3];}// 計算W[16]-W[67]for (int j = 16; j < 68; j++) {uint32_t temp = W[j-16] ^ W[j-9] ^ ROTL(W[j-3], 15);W[j] = P1(temp) ^ ROTL(W[j-13], 7) ^ W[j-6];}// 計算W1[0]-W1[63]for (int j = 0; j < 64; j++) {W1[j] = W[j] ^ W[j+4];}// 2. 寄存器初始化(A-H)uint32_t A = context->state[0];uint32_t B = context->state[1];uint32_t C = context->state[2];uint32_t D = context->state[3];uint32_t E = context->state[4];uint32_t F = context->state[5];uint32_t G = context->state[6];uint32_t H = context->state[7];// 3. 64輪迭代(嚴格遵循標準)for (int j = 0; j < 64; j++) {uint32_t SS1, SS2, TT1, TT2;// 常量選擇(關鍵修正)uint32_t TJ = (j < 16) ? 0x79CC4519 : 0x7A879D8A;// 計算SS1/SS2(修正了TJ參數)SS1 = ROTL(ROTL(A, 12) + E + ROTL(TJ, j % 32), 7);SS2 = SS1 ^ ROTL(A, 12);// 計算TT1/TT2(使用內聯函數)if (j < 16) {TT1 = FF0(A, B, C) + D + SS2 + W1[j];TT2 = GG0(E, F, G) + H + SS1 + W[j];} else {TT1 = FF1(A, B, C) + D + SS2 + W1[j];TT2 = GG1(E, F, G) + H + SS1 + W[j];}// 更新寄存器(嚴格順序)D = C;C = ROTL(B, 9);B = A;A = TT1;H = G;G = ROTL(F, 19);F = E;E = P0(TT2);}// 4. 更新最終狀態(與初始IV異或)context->state[0] ^= A;context->state[1] ^= B;context->state[2] ^= C;context->state[3] ^= D;context->state[4] ^= E;context->state[5] ^= F;context->state[6] ^= G;context->state[7] ^= H;
}// 更新數據(可分多次調用)
void SM3Update(SM3Context *context, const uint8_t *data, size_t length) {context->totalLength += length * 8; // 更新總位數(字節轉位)// 處理緩沖區中的剩余空間if (context->bufferLength > 0) {size_t copySize = MIN(64 - context->bufferLength, length);memcpy(context->buffer + context->bufferLength, data, copySize);context->bufferLength += copySize;data += copySize;length -= copySize;if (context->bufferLength == 64) {SM3Compress(context, context->buffer);context->bufferLength = 0;}}// 處理完整塊while (length >= 64) {SM3Compress(context, data);data += 64;length -= 64;}// 緩存剩余數據if (length > 0) {memcpy(context->buffer, data, length);context->bufferLength = length;}
}// 完成哈希計算
void SM3Final(SM3Context *context, uint8_t output[32]) {// 計算填充長度(SM3標準:補位1 + k個0 + 64位長度)size_t totalBits = context->totalLength;size_t paddingBits = (context->bufferLength < 56) ?(56 - context->bufferLength) :(120 - context->bufferLength);// 構建填充數據uint8_t padding[128] = {0};padding[0] = 0x80; // 補位起始位(二進制10000000)// 添加填充SM3Update(context, padding, paddingBits);// 添加消息長度(大端序64位)uint64_t bitCount = CFSwapInt64HostToBig(totalBits);SM3Update(context, (uint8_t *)&bitCount, 8);// 確保最后一個塊被處理if (context->bufferLength > 0) {memset(context->buffer + context->bufferLength, 0, 64 - context->bufferLength);SM3Compress(context, context->buffer);}// 輸出最終哈希(256位,大端序)for (int i = 0; i < 8; i++) {output[i*4]     = (uint8_t)(context->state[i] >> 24);output[i*4 + 1] = (uint8_t)(context->state[i] >> 16);output[i*4 + 2] = (uint8_t)(context->state[i] >> 8);output[i*4 + 3] = (uint8_t)(context->state[i]);}
}// Objective-C 封裝接口
@implementation SM3Encryptor+ (NSData *)hashWithData:(NSData *)inputData {SM3Context context;SM3Init(&context);// 處理輸入數據SM3Update(&context, inputData.bytes, inputData.length);// 獲取結果uint8_t output[32];SM3Final(&context, output);return [NSData dataWithBytes:output length:32];
}+ (NSString *)hexStringWithData:(NSData *)inputData {NSData *hashData = [self hashWithData:inputData];const uint8_t *bytes = (const uint8_t *)hashData.bytes;NSMutableString *hex = [NSMutableString string];for (NSUInteger i = 0; i < hashData.length; i++) {[hex appendFormat:@"%02X", bytes[i]];}return [hex copy];
}+ (NSString *)hexStringWithInput:(NSString *)inputStr {NSData *inputData = [inputStr dataUsingEncoding:NSUTF8StringEncoding];NSData *hashData = [self hashWithData:inputData];const uint8_t *bytes = (const uint8_t *)hashData.bytes;NSMutableString *hex = [NSMutableString string];for (NSUInteger i = 0; i < hashData.length; i++) {[hex appendFormat:@"%02X", bytes[i]];}return [hex copy];
}@end

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

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

相關文章

[逆向工程]什么是TEB 與 PEB(二十九)

[逆向工程]什么是TEB 與 PEB(二十九) 一、引言:為什么需要了解 TEB/PEB? 在 Windows 系統開發、調試或逆向工程中,TEB(Thread Environment Block) 和 PEB(Process Environment Block) 是理解程序執行機制的關鍵。它們如同進程與線程的“身份證”,存儲了從內存布局到…

逆向分析貝殼網人機驗證JS加密邏輯

引言 在數據爬取和自動化測試過程中&#xff0c;人機驗證&#xff08;如滑塊、點選、短信驗證等&#xff09;是常見的反爬手段。貝殼網&#xff08;ke.com&#xff09;作為國內領先的房產平臺&#xff0c;其人機驗證機制較為復雜&#xff0c;涉及前端JS加密、動態Token、行為檢…

Vue3 + Element Plus中el-table加載狀態分析

在 Vue 3 中&#xff0c;當 onMounted 鉤子被觸發時&#xff0c;父組件的 DOM 已經掛載完成&#xff0c;但子組件&#xff08;如 el-table&#xff09;可能尚未完成其內部渲染。具體分析如下&#xff1a; 1. onMounted 的執行時機 父組件掛載完成&#xff1a;onMounted 表示當前…

OpenCV圖像拼接技術詳解:從特征匹配到全景合成

本文將詳細介紹如何使用OpenCV實現兩幅圖像的自動拼接&#xff0c;涵蓋特征提取、單應性矩陣計算和圖像融合等關鍵技術。 一、圖像拼接概述 圖像拼接是將多張有重疊區域的圖像合并成一幅全景圖的技術&#xff0c;廣泛應用于全景攝影、衛星圖像處理、醫學影像等領域。其核心技術…

如何通過 5 種方式向 Android 手機添加音樂

想把音樂添加到你的安卓手機&#xff0c;然后隨時隨地無需網絡連接就能欣賞你喜愛的音樂嗎&#xff1f;這不再是麻煩。現在&#xff0c;你可以按照本指南中的有效方法&#xff0c;將音樂添加到你的安卓手機上。讓我們在安卓手機上聆聽我們美妙的歌曲吧。 第 1 部分&#xff1a;…

VS Code 項目中的 .vscode 目錄詳解

VS Code 項目中的 .vscode 目錄詳解 .vscode 目錄是 VS Code 項目的核心配置中心&#xff0c;它包含特定于當前項目的配置&#xff0c;這些配置覆蓋全局設置&#xff0c;確保團隊成員獲得一致的開發環境體驗。 .vscode 目錄中的核心文件 文件名作用是否應納入版本控制settin…

Ubuntu22.04安裝opengauss并配置遠程訪問、JDBC連接

內容概括 最近在研究怎么在ubuntu服務器環境下使用opengauss&#xff0c;看了下官方下載地址沒有適配ubuntu的安裝包。仔細翻了下官方文檔&#xff0c;發現安裝指南里有提供一個deb包安裝方案&#xff0c;有適配ubuntu&#xff0c;經過實踐可行&#xff0c;于是記錄下來給有需要…

國產智能體“雙子星”:實在Agent vs Manus(核心架構與技術實現路徑對比)

2025年&#xff0c;人工智能領域迎來重要轉折點——大模型的光環逐漸消散&#xff0c;落地應用成為行業焦點。 正如業內人士所言&#xff1a;“2023年&#xff0c;大家普遍覺得要買一個大模型&#xff0c;但訓練完了怎么用起來&#xff0c;大家一頭霧水。” 在這一背景下&…

pgAdmin 4 連接 postgreSQL

環境如下&#xff1a; 宿主機為Windows 11postgreSQL安裝在宿主機上的Linux虛機中&#xff0c;Hypervisor是VirtualBoxpgAdmin 4 已安裝在宿主機上 本文講述&#xff1a;如何通過宿主機上的pgAdmin 連接到虛擬機中的PG。 設置監聽 默認的PG監聽主機為localhost&#xff0c;…

HTTP 緩存策略:強緩存與協商緩存的深入解析

在HTTP緩存策略中&#xff0c;強緩存和協商緩存是兩種常用的機制&#xff0c;用于減少數據傳輸和提高網頁加載速度。它們通過在客戶端和服務器之間建立緩存來避免不必要的網絡請求&#xff0c;從而優化性能并提高用戶體驗。本文將詳細介紹這兩種緩存策略的原理、優勢和適用場景…

Node.js 中的 Token 認證機制詳解

文章目錄 Node.js 中的 Token 認證機制詳解1. Token 認證基礎1.1 什么是 Token 認證&#xff1f;1.2 Token 認證流程 2. JWT (JSON Web Token) 實現2.1 安裝依賴2.2 生成 Token2.3 驗證 Token 中間件 3. 完整實現示例3.1 登錄接口3.2 受保護的路由 4. Token 安全最佳實踐5. Tok…

23 - HaLoAttention模塊

論文《Scaling Local Self-Attention for Parameter Efficient Visual Backbones》 1、作用 HaloNet通過引入Haloing機制和高效的注意力實現&#xff0c;在圖像識別任務中達到了最先進的準確性。這些模型通過局部自注意力機制&#xff0c;有效地捕獲像素間的全局交互&#xf…

2025Mybatis最新教程(五)

第5章 ORM映射 5.1 MyBatis自動ORM失效 MyBatis只能自動維護庫表”列名“與”屬性名“相同時的對應關系,二者不同時,無法自動ORM。 自動ORM失效建表 create table t_managers(mgr_id int primary key auto_increment,mgr_name varchar(50),mgr_pwd varchar(50) ); 添加數據…

解決lombok注解失效問題

Lombok 注解失效是 Java 開發中的常見問題&#xff0c;通常由依賴配置、IDE 支持或構建工具設置引起。最近在拉取別人springboot3jdk21版本的項目時遇到了lombok注解失效&#xff0c;導致項目無法啟動的問題&#xff0c;以下是我的解決方案&#xff1a; 首先檢查idea 的lombok…

3分鐘搭建LarkXR實時云渲染PaaS平臺,實現各類3D/XR應用的一鍵推流

LarkXR是由Paraverse平行云自主研發的去中心化實時云渲染平臺&#xff0c;以其卓越的性能和豐富完備的功能插件&#xff0c;引領3D/XR云化行業風向標。LarkXR適用于3D/XR開發者、設計師、終端用戶等創新用戶&#xff0c;可以在零硬件負擔下&#xff0c;輕松實現超高清低時延的3…

vue3 watch監視詳解

watch監視 一 &#xff1a;watch監視{ref}定義的基本類型結構 <template><div class"person"><h1>情況一:watch監視{ref}定義的基本類型結構</h1><h1>當前的和為{{ sum }}</h1><button click"changeSum">點我…

TensorFlow Serving學習筆記2: 模型服務

本文深入剖析 TensorFlow Serving 的核心架構與實現機制&#xff0c;結合源碼分析揭示其如何實現高可用、動態更新的生產級模型服務。 一、TensorFlow Serving 核心架構 1.1 分層架構設計 TensorFlow Serving 采用模塊化分層設計&#xff0c;各組件職責分明&#xff1a; 組件…

共享云桌面為什么能打敗傳統電腦

近年來&#xff0c;隨著云桌面技術的快速發展&#xff0c;共享云桌面作為一種新型的計算模式&#xff0c;正在逐步改變人們的工作和生活方式。它憑借其獨特的優勢&#xff0c;正在逐步取代傳統電腦&#xff0c;成為企業和個人用戶的新選擇。之所以在部分場景中展現出替代傳統電…

B站PWN教程筆記-12

完結撒花。 今天還是以做題為主。 fmtstruaf 格式化字符串USER AFTER FREE 首先補充一個背景知識&#xff0c;指針也是有數據類型的&#xff0c;不同數據類型的指針xx&#xff0c;所加的字節數也不一樣&#xff0c;其實是指針指的項目的下一項。如int a[20]&#xff0c;a是…

零基礎設計模式——總結與進階 - 3. 學習資源與下一步

第五部分&#xff1a;總結與進階 - 3. 學習資源與下一步 到這里&#xff0c;你已經完成了設計模式主要內容的學習。但這僅僅是一個開始&#xff0c;設計模式的精髓在于實踐和持續學習。本節將為你提供一些優質的學習資源和后續學習的建議&#xff0c;幫助你在這條道路上走得更…