截至2015年1月27日,所采用版本为1.2.8,源地址:https://github.com/nxtbgthng/OAuth2Client
开源OAuth2.0 在ios上面的library OAuth2Client不算是一个好用的库,很多东西需要手工改,不过还好它底层的东西写的不错,给自己减轻了工作量。
但在最近测试refresh token的时候,却出现了一个让人非常抓狂的问题,就是有时候无法自动refresh token。
As we all know,从服务器获取token的时候,除了拿到一个access token之外,还会拿到refresh token和expire time。意思是如果超过了expire time,就用refresh token重新去获取token。但是问题就出来在,我第一次测试的时候,用虚拟机,wireshark监控,非常满意,超过expire time之后的第一次网络请求,它会自动先请求一次token。可是后来在真机上测试,加了一个每10秒上传一次位置的功能(同样需要token),却出现了问题,不能自动refresh。
后来再次在虚拟机上测试时,也是时好时坏,有时候能refresh,有时候却压根没有。后来自己排查,发现问题出现在OAuth2Client - Connection - NXOAuth2Connection.m中的-createConnection方法,原文如下:
- (NSURLConnection *)createConnection;
{
// if the request is a token refresh request don't sign it and don't check for the expiration of the token (we know that already)
NSString *oauthAuthorizationHeader = nil;
if (client.accessToken &&
![[requestParameters objectForKey:@"grant_type"] isEqualToString:@"refresh_token"]) {
// if token is expired don't bother starting this connection.
NSDate *tenSecondsAgo = [NSDate dateWithTimeIntervalSinceNow:(+10)];
NSDate *tokenExpiresAt = client.accessToken.expiresAt;
if (client.accessToken.refreshToken && [tenSecondsAgo earlierDate:tokenExpiresAt] == tokenExpiresAt) {
[self cancel];
[client refreshAccessTokenAndRetryConnection:self];
return nil;
}
NSString *tokenType = client.accessToken.tokenType;
if (tokenType == nil) {
tokenType = client.tokenType;
}
if (tokenType == nil) {
tokenType = @"OAuth";
}
oauthAuthorizationHeader = [NSString stringWithFormat:@"%@ %@", tokenType, client.accessToken.accessToken];
}
NSMutableURLRequest *startRequest = [request mutableCopy];
[self applyParameters:requestParameters onRequest:startRequest];
if (oauthAuthorizationHeader) {
[startRequest setValue:oauthAuthorizationHeader forHTTPHeaderField:@"Authorization"];
}
if (client.userAgent && ![startRequest valueForHTTPHeaderField:@"User-Agent"]) {
[startRequest setValue:client.userAgent forHTTPHeaderField:@"User-Agent"];
}
if (client.acceptType) {
[startRequest setValue:client.acceptType forHTTPHeaderField:@"Accept"];
}
NSURLConnection *aConnection = [[NSURLConnection alloc] initWithRequest:startRequest delegate:self startImmediately:NO]; // don't start yet
[aConnection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; // let's first schedule it in the current runloop. (see http://github.com/soundcloud/cocoa-api-wrapper/issues#issue/2 )
[aConnection start]; // now start
if (!sendConnectionDidEndNotification) [[NSNotificationCenter defaultCenter] postNotificationName:NXOAuth2ConnectionDidStartNotification object:self];
sendConnectionDidEndNotification = YES;
return aConnection;
}
注意看其中的
NSDate *tenSecondsAgo = [NSDate dateWithTimeIntervalSinceNow:(+10)];
NSDate *tokenExpiresAt = client.accessToken.expiresAt;
if (client.accessToken.refreshToken && [tenSecondsAgo earlierDate:tokenExpiresAt] == tokenExpiresAt) {
[self cancel];
[client refreshAccessTokenAndRetryConnection:self];
return nil;
}
这个判断的意思是,如果当前时间超出expireTime超过10s,就执行refreshtoken,不然就继续,不执行refresh。这让我非常难以理解,因为即使你只超出了1s,服务器也会判断你token失效,请求失败的,为什么这个地方要延长10s呢?而且经过我的测试,如果我在这10s内发送请求,服务器会拒绝,而OAuth2Client自己也不会再次refresh了!!
也就是说,如果你的app在expire的时间之前,或者expire的时间10秒之后,发送请求都没问题,但偏偏在这10s内请求就会出错,而且没有改错的机制!!!
十分不明白为什么,于是就把+10改成了-10,变成了在expiretime的10s之前,就发送refresh token,保证refresh的成功。
经过测试,成功!