博客園官方API 花樣作死封裝網路層(iOS或OSX) === 前一段時間通過孤獨的貓咪神瞭解到博客園有官方API,據說今年四月份下旬的樣子推出吧。(道聽途說!)...我小小的申請去測試了下,打算也用博客園官方的API寫一點兒東西,例如OSX和iOS的小軟體。。在之前博客園官方的API也是公開的,舊
博客園官方API--花樣作死封裝網路層(iOS或OSX)
前一段時間通過孤獨的貓咪神瞭解到博客園有官方API,據說今年四月份下旬的樣子推出吧。(道聽途說!)...我小小的申請去測試了下,打算也用博客園官方的API寫一點兒東西,例如OSX和iOS的小軟體。。在之前博客園官方的API也是公開的,舊的一套,HTTP的,返回的數據是XML,在iOS端下SAX解析有點麻煩。。。有點兒影響性能。。。
博客園官方API是HTTPS請求的,我在iOS下用AFN和NSURLSession花樣作死實現了網路層,哈哈本來標題是這麼長的,想想太長了,改改,文章寫了很久,內容不多,主要是研究AFN請求博客園的介面的時候費了點兒時間,而且最近也有點忙,,,都是晚上回家再研究的。。。HTTPS,證書可能是非CA或自簽名或伺服器HTTPS版本為1.1,反正只要是有這幾點,不管是博客園官方的API還是任何這樣的介面,首先都先設置一下ATS吧,再看這篇文章下麵的實現吧。。。 在iOS端用AFN有點兒麻煩,相反用蘋果自帶的NSURLSession實現起來還挺簡單的,因為有個代理和伺服器挑戰的功能,下麵會貼上代碼(~博客園給我的秘鑰隱藏了,遵守規定,保守秘密,dudu也看不到。。。哈哈~~)。
申請了開發者許可權後會給一個賬號和密碼,然後得算出AccessToken,下麵就說這三個事兒吧,第一個是算出AccessToken,第二個說說用NSURLSession寫網路層,第三個用AFN實現網路層,我相信很多朋友都在找AFN實現HTTPS請求的代碼吧,我就找過,網上真不是很多。。。因為蘋果官方的NSURLSession處理這樣的情況真的很好用!
一、算出AccessToken
演算法直接看dudu的文章吧,下麵是我用C實現的演算法。。。
static int base64_decode_map[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32 - 47
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48 - 63
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64 - 79
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80 - 95
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96 - 111
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112 - 127
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 - 143
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144 - 159
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160 - 175
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176 - 191
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192 - 207
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208 - 223
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224 - 239
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 240 - 255
};
char *base64_decode(const char* input, char *output)
{
output[0] = '\0';
if (input == NULL || output == NULL)
return output;
int input_len = (int)strlen(input);
if (input_len < 4 || input_len % 4 != 0)
return output;
// 0xFC -> 11111100
// 0x03 -> 00000011
// 0xF0 -> 11110000
// 0x0F -> 00001111
// 0xC0 -> 11000000
char *p = (char*)input;
char *p_out = output;
char *p_end = (char*)input + input_len;
for (; p < p_end; p += 4) {
*p_out++ = ((base64_decode_map[p[0]] << 2) & 0xFC) | ((base64_decode_map[p[1]] >> 4) & 0x03);
*p_out++ = ((base64_decode_map[p[1]] << 4) & 0xF0) | ((base64_decode_map[p[2]] >> 2) & 0x0F);
*p_out++ = ((base64_decode_map[p[2]] << 6) & 0xC0) | (base64_decode_map[p[3]]);
}
if (*(input + input_len - 2) == '=') {
*(p_out - 2) = '\0';
} else if (*(input + input_len - 1) == '=') {
*(p_out - 1) = '\0';
}
return output;
}
int main(int argc, const char * argv[]) {
char AccessToken[150];
strcpy (AccessToken,"Your ClientId");
strcat (AccessToken,":");
strcat (AccessToken,"Your ClientSercret");
int num[150];
for(int i=0;i<strlen(AccessToken);i++) num[i]=AccessToken[i];
for (int i = 0; i < strlen(AccessToken); i++) printf("%d",num[i]);
char decode[200];
char *input;
base64_decode(num, decode);
printf("%s",decode);
return 0;
}
演算法在dudu的文章里都有,上面的代碼不做過多講解了,只是蛋疼寫寫擺了,因為在oc下兩句代碼實現上面的功能,並且AFN內部也封裝了這個演算法,下麵看看oc的一句代碼,AFN就就不貼上來了
NSString *authString = [[[NSString stringWithFormat:@"%@:%@",ClientId,ClientSercret] dataUsingEncoding:NSUTF8StringEncoding] base64Encoding];
二、NSURLSession實現網路層
- (NSURLSession *)session {
if (_session == nil) {
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSString *str = @"Basic 這裡填寫通過將clientid和clientSecret用冒號拼接起來,然後轉成ASCII碼,然後再BASE64加密,計算出authString";
// 設置請求頭
config.HTTPAdditionalHeaders = @{@"Authorization":str};
_session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
}
return _session;
}
- (void)viewDidLoad {
[super viewDidLoad];
// ClientId
NSString *ClientId = @"你的ClientId";
// ClientSercret
NSString *ClientSercret = @"你的ClientSercret";
// 將clientid和clientSecret用冒號拼接起來,然後轉成ASCII碼,然後再BASE64加密,計算出authString
NSString *authString = [[[NSString stringWithFormat:@"%@:%@",ClientId,ClientSercret] dataUsingEncoding:NSUTF8StringEncoding] base64Encoding];
authString = [NSString stringWithFormat: @"Basic %@", authString];
//
NSLog(@"%@",authString);
NSURL *url = [NSURL URLWithString:@"你的API介面"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPBody = [@"你要攜帶的參數" dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = @"POST";
// [request addValue:authString forHTTPHeaderField:@"Authorization"];
[[self.session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString *mJson = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSLog(@"%@",mJson);
NSLog(@"%@",response);
NSLog(@"%@",error);
}] resume];
}
//挑戰伺服器。伺服器使用TLS1.0的協議,還需要開啟ATS
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
// 判斷認證方式(是用戶名密碼還是證書認證)
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
// 設置信任伺服器的證書
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
completionHandler(0,credential);
}
}
一些小小的tips
- NSURLSessionConfiguration作用
- 可以設置請求頭(Content-Type Range User-Agent Authorization等)
- 可以設置最大連接數
- 可以設置超時時長,緩存策略
- 類方法,創建對象
- defaultSessionConfiguration
- 會使用磁碟緩存,賬戶信息存儲到鑰匙鏈,如果有cookie會攜帶cookie
- ephemeralSessionConfiguration
- 沒有磁碟緩存,不存儲賬戶信息,不攜帶cookie
- 數據存儲在記憶體,速度快,如果要存儲到磁碟需要自己寫
- backgroundSessionConfigurationWithIdentifier
- 在一個單獨的進程上下載
- app進入後臺或終止之後,依然可以繼續下載
- 屬性
- HTTPAdditionalHeaders:添加請求頭
- requestCachePolicy:緩存策略
- timeoutIntervalForRequest:請求的超時時長
- allowsCellularAccess:運行蜂窩網路訪問
- HTTPMaximumConnectionsPerHost:主機的最大連接數
搞iOS的朋友註意一下代理的方法
接受伺服器的挑戰,信任伺服器
- 發送請求之前先挑戰伺服器(進行HTTPS的協商)
- https的協商
- 信任伺服器返回的證書(公鑰)
- 客戶端產生的隨機秘鑰
- 公鑰對 隨機秘鑰進行對稱加密(rsa)
- 把用公鑰加密的隨機秘鑰發送給伺服器
- 使用隨機秘鑰加密和伺服器交互
三、最後看看花樣作死用AFN實現,我還是建議用NSURLSession比較簡單
下麵貼出來的是網路層封裝的一個GET方法,其他的都一樣
+ (void)GET:(NSString *)urlString parameters:(id)parameters progress:(void(^)(NSProgress *downloadProgress))progress success:(void(^)(id responseObject))success failure:(void(^)(NSError *error))failure {
AFHTTPSessionManager *mgr =[AFHTTPSessionManager manager];
ERAuthorizationManager *authorMgr = [ERAuthorizationManager sharedERAuthorizationManager];
NSString *access_token =[NSString stringWithFormat:@"%@%@",@"Bearer ",[authorMgr getAccess_token]];
[mgr.requestSerializer setValue:access_token forHTTPHeaderField:@"Authorization"];
NSLog(@"%@",mgr.requestSerializer.HTTPRequestHeaders);
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
securityPolicy.allowInvalidCertificates = YES;
securityPolicy.validatesDomainName = NO;
[securityPolicy setValidatesDomainName:NO];
mgr.securityPolicy = securityPolicy;
[mgr GET:urlString parameters:nil progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success) {
success(responseObject);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure) {
failure(error);
}
NSLog(@"%ld",error.code);
}];
}
下麵說說其中幾句關鍵代碼,並不複雜也不難,就是設置一下屬性就OK。
設置允許無效簽名證書(特別是這一步,十分重要呀!!!)
securityPolicy.allowInvalidCertificates = YES;
設置domain為no
securityPolicy.validatesDomainName = NO;
設置不需要驗證功能變數名稱
[securityPolicy setValidatesDomainName:NO];
好吧,其實沒有什麼難的地方,在網路層用AFN的時候查了查頭文件才知道一些屬性。。。因為不常用吧。。特別是AFN實現的HTTPS設置允許無效簽名證書的屬性。。
最後期待博客園官方的API能儘快上線吧,博客園重度用戶的期待。。。
最後,感謝孤獨的貓咪神對我細心的幫助,也感謝博客園。。。謝謝!!!