1
This commit is contained in:
@@ -6,7 +6,8 @@
|
||||
#import "KBNetworkManager.h"
|
||||
#import "AFNetworking.h"
|
||||
#import "KBAuthManager.h"
|
||||
|
||||
#import "KBUserSessionManager.h"
|
||||
#import "KBSignUtils.h"
|
||||
NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
|
||||
@interface KBNetworkManager ()
|
||||
@@ -28,8 +29,11 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
_timeout = 10.0;
|
||||
// 默认请求头:Accept 任意类型 + 使用项目多语言管理器设置 Accept-Language
|
||||
NSString *lang = [KBLocalizationManager shared].currentLanguageCode ?: KBLanguageCodeEnglish;
|
||||
NSString *token = [KBUserSessionManager shared].accessToken ? [KBUserSessionManager shared].accessToken : @"";
|
||||
|
||||
_defaultHeaders = @{
|
||||
@"Accept": @"*/*",
|
||||
@"auth-token" : token,
|
||||
@"Accept-Language": lang
|
||||
};
|
||||
// 设置基础域名,路径可相对该地址拼接
|
||||
@@ -38,12 +42,49 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)getSignWithParare:(NSDictionary *)bodyParams{
|
||||
|
||||
NSString *appId = @"loveKeyboard";
|
||||
NSString *secret = @"kZJM39HYvhxwbJkG1fmquQRVkQiLAh2H"; // 和服务端保持一致
|
||||
NSString *timestamp = [KBSignUtils currentTimestamp];
|
||||
NSString *nonce = [KBSignUtils generateNonceWithLength:16];
|
||||
// 1. 组装参与签名的所有参数
|
||||
NSMutableDictionary<NSString *, NSString *> *signParams = [NSMutableDictionary dictionary];
|
||||
signParams[@"appId"] = appId;
|
||||
signParams[@"timestamp"] = timestamp;
|
||||
signParams[@"nonce"] = nonce;
|
||||
// 把 body 里的字段也加入签名参数
|
||||
[bodyParams enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
if ([obj isKindOfClass:[NSString class]]) {
|
||||
signParams[key] = obj;
|
||||
} else {
|
||||
signParams[key] = [obj description];
|
||||
}
|
||||
}];
|
||||
NSString *sign = [KBSignUtils signWithParams:signParams secret:secret];
|
||||
|
||||
// 将签名相关字段合并进默认请求头
|
||||
NSMutableDictionary<NSString *, NSString *> *headers =
|
||||
[self.defaultHeaders mutableCopy] ?: [NSMutableDictionary dictionary];
|
||||
|
||||
if (sign.length > 0) {
|
||||
headers[@"X-Sign"] = sign;
|
||||
}
|
||||
headers[@"X-App-Id"] = appId;
|
||||
headers[@"X-Timestamp"] = timestamp;
|
||||
headers[@"X-Nonce"] = nonce;
|
||||
|
||||
// 触发 copy 语义,确保对外仍是不可变字典
|
||||
self.defaultHeaders = headers;
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
- (NSURLSessionDataTask *)GET:(NSString *)path
|
||||
parameters:(NSDictionary *)parameters
|
||||
headers:(NSDictionary<NSString *,NSString *> *)headers
|
||||
completion:(KBNetworkCompletion)completion {
|
||||
[self getSignWithParare:parameters];
|
||||
if (![self ensureEnabled:completion]) return nil;
|
||||
NSString *urlString = [self buildURLStringWithPath:path];
|
||||
if (!urlString) { [self fail:KBNetworkErrorInvalidURL completion:completion]; return nil; }
|
||||
@@ -67,6 +108,8 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
jsonBody:(id)jsonBody
|
||||
headers:(NSDictionary<NSString *,NSString *> *)headers
|
||||
completion:(KBNetworkCompletion)completion {
|
||||
[self getSignWithParare:jsonBody];
|
||||
|
||||
if (![self ensureEnabled:completion]) return nil;
|
||||
NSString *urlString = [self buildURLStringWithPath:path];
|
||||
if (!urlString) { [self fail:KBNetworkErrorInvalidURL completion:completion]; return nil; }
|
||||
@@ -87,6 +130,7 @@ NSErrorDomain const KBNetworkErrorDomain = @"com.company.keyboard.network";
|
||||
parameters:(NSDictionary *)parameters
|
||||
headers:(NSDictionary<NSString *,NSString *> *)headers
|
||||
completion:(KBNetworkDataCompletion)completion {
|
||||
[self getSignWithParare:parameters];
|
||||
if (!self.isEnabled) {
|
||||
NSError *e = [NSError errorWithDomain:KBNetworkErrorDomain
|
||||
code:KBNetworkErrorDisabled
|
||||
|
||||
32
CustomKeyboard/Utils/KBSignUtils.h
Normal file
32
CustomKeyboard/Utils/KBSignUtils.h
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// KBSignUtils.h
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/4.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface KBSignUtils : NSObject
|
||||
|
||||
/// URL 编码(与后端保持一致)
|
||||
+ (NSString *)urlEncode:(NSString *)value;
|
||||
|
||||
/// HMAC-SHA256,返回十六进制小写字符串
|
||||
+ (NSString *)hmacSHA256:(NSString *)data secret:(NSString *)secret;
|
||||
|
||||
/// 生成签名:传入参与签名的所有参数(不含 sign 自身)
|
||||
+ (NSString *)signWithParams:(NSDictionary<NSString *, NSString *> *)params
|
||||
secret:(NSString *)secret;
|
||||
|
||||
/// 秒级时间戳(字符串)
|
||||
+ (NSString *)currentTimestamp;
|
||||
|
||||
/// 简单 nonce 生成(默认 16 位)
|
||||
+ (NSString *)generateNonceWithLength:(NSUInteger)length;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
92
CustomKeyboard/Utils/KBSignUtils.m
Normal file
92
CustomKeyboard/Utils/KBSignUtils.m
Normal file
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// KBSignUtils.m
|
||||
// keyBoard
|
||||
//
|
||||
// Created by Mac on 2025/12/4.
|
||||
//
|
||||
|
||||
#import "KBSignUtils.h"
|
||||
#import <CommonCrypto/CommonCrypto.h>
|
||||
|
||||
@implementation KBSignUtils
|
||||
|
||||
+ (NSString *)urlEncode:(NSString *)value {
|
||||
if (!value) return @"";
|
||||
// 和 Swift 里的 .urlQueryAllowed 类似
|
||||
NSCharacterSet *allowed = [NSCharacterSet URLQueryAllowedCharacterSet];
|
||||
NSString *encoded = [value stringByAddingPercentEncodingWithAllowedCharacters:allowed];
|
||||
return encoded ?: value;
|
||||
}
|
||||
|
||||
+ (NSString *)hmacSHA256:(NSString *)data secret:(NSString *)secret {
|
||||
const char *cKey = [secret cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
|
||||
|
||||
CCHmac(kCCHmacAlgSHA256,
|
||||
cKey,
|
||||
strlen(cKey),
|
||||
cData,
|
||||
strlen(cData),
|
||||
cHMAC);
|
||||
|
||||
// 转十六进制小写字符串
|
||||
NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
|
||||
for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++) {
|
||||
[output appendFormat:@"%02x", cHMAC[i]];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
+ (NSString *)signWithParams:(NSDictionary<NSString *, NSString *> *)params
|
||||
secret:(NSString *)secret {
|
||||
|
||||
// 1. 过滤空值 & sign 自身
|
||||
NSMutableDictionary<NSString *, NSString *> *filtered = [NSMutableDictionary dictionary];
|
||||
[params enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL *stop) {
|
||||
if (!obj || obj.length == 0) return;
|
||||
if ([[key lowercaseString] isEqualToString:@"sign"]) return;
|
||||
filtered[key] = obj;
|
||||
}];
|
||||
|
||||
// 2. 按 key 字典序排序
|
||||
NSArray<NSString *> *sortedKeys = [[filtered allKeys] sortedArrayUsingSelector:@selector(compare:)];
|
||||
|
||||
// 3. 拼接 key=value&key2=value2...&secret=xxx
|
||||
NSMutableArray<NSString *> *components = [NSMutableArray array];
|
||||
for (NSString *key in sortedKeys) {
|
||||
NSString *value = filtered[key] ?: @"";
|
||||
NSString *encodedValue = [self urlEncode:value];
|
||||
NSString *part = [NSString stringWithFormat:@"%@=%@", key, encodedValue];
|
||||
[components addObject:part];
|
||||
}
|
||||
|
||||
NSString *encodedSecret = [self urlEncode:secret];
|
||||
NSString *secretPart = [NSString stringWithFormat:@"secret=%@", encodedSecret];
|
||||
[components addObject:secretPart];
|
||||
|
||||
NSString *dataString = [components componentsJoinedByString:@"&"];
|
||||
|
||||
// 4. HMAC-SHA256
|
||||
NSString *sign = [self hmacSHA256:dataString secret:secret];
|
||||
return sign;
|
||||
}
|
||||
|
||||
+ (NSString *)currentTimestamp {
|
||||
NSTimeInterval ts = [[NSDate date] timeIntervalSince1970];
|
||||
long long seconds = (long long)ts;
|
||||
return [NSString stringWithFormat:@"%lld", seconds];
|
||||
}
|
||||
|
||||
+ (NSString *)generateNonceWithLength:(NSUInteger)length {
|
||||
// 用 UUID 生成,去掉 - , 再截取前 length 位
|
||||
NSString *uuid = [[NSUUID UUID].UUIDString stringByReplacingOccurrencesOfString:@"-" withString:@""];
|
||||
if (length == 0 || length > uuid.length) {
|
||||
return uuid;
|
||||
}
|
||||
return [uuid substringToIndex:length];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user