2014-03-01 153 views
1

我有一個相當簡單的授權/身份驗證應用程序,並且需要一些關於如何處理授權生命週期的幫助。下面是它的JIST:iOS:使用NSURLSession進行身份驗證和令牌過期

  1. 用戶輸入他們的憑據登錄服務器用在鑰匙串
  2. 令牌處於「授權」頭用於每個後續的URL請求令牌並存儲響應(REST調用)
  3. 令牌最終到期,服務器將與401個狀態碼
  4. 迴應此時,用戶需要通過登錄畫面重新進行身份驗證,所以我們可以得到一個新的令牌,然後重試請求

下面是一些代碼,代表我現在可以提出的最好的代碼。

static NSString * _authorizationToken; 
@implementation MyRestfulModel 
+ (id) sharedRestfulModel 
{ 
    // singleton model shared across view controllers 
    static MyRestfulModel * _sharedModel = nil; 
    @synchronized(self) { 
     if (_sharedModel == nil) 
      _sharedModel = [[self init] alloc]; 
    } 
    return _sharedModel; 
} 

+ (NSString *) authorizationToken 
{ 
    if (!_authorizationToken) 
     _authorizationToken = @""; 
    return _authorizationToken; 
} 

+ (void) setAuthorizationToken: (NSString *token) 
{ 
    _authorizationToken = token; 
} 

-(void) doSomeRestfulCall: (NSString *) restURL 
     completionHandler: (void (^)(NSData * data)) callback 
{ 
    // construct url path and request 
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:restURL]; 
    [request setValue:MyRestfulModel.authorizationToken forHTTPHeaderField:@"Authorization"]; 
    [[[NSURLSession sharedSession] dataTaskWithRequest: request 
            completionHandler:^(NSData *data, NSURLResponse * response, NSError * error) { 

        NSHTTPResponse * httpResponse = (NSHTTPResponse *) response; 
        if([httpResponse statusCode] == 401) { // WHAT TO DO HERE ? 

         dispatch_sync(dispatch_get_main_queue(), ^{ 
          MyAppDelegate * delegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
          [delegate showLoginViewController callback:^(NSString * username, NSString * newToken) { 
           // recreate the NSURLSession and NSURLConfiguration with the new token and retry 
           MyRestfulModel.authenticationToken = token; 
           [self doSomeRestfulCall:callback]; 
          }]; 
         } 
         return; 
        } else { 
         callback(data); 
        } 
    }] resume]; 
} 

我希望做這樣使得視圖控制器從來不需要擔心重試呼叫由於令牌到期,所有可怕的會話處理和認證可以通過一個對象來完成。

我正在考慮嘗試使用NSURLSessionDelegate,但我無法弄清楚didReceiveChallenge部分w.r.t.彈出登錄視圖控制器。我正在考慮的另一個選擇是添加一個類似於完成處理程序的「retryHandler」,該程序將在用戶重新驗證後調用。

回答

0

我想我找到了一個很好的解決方案。以「doSomeRestfulCall」方法爲例。

-(void) doSomeRestfullCall: (NSString *) restURL 
     completionHandler: (void (^)(NSData * data)) callback 
{ 
    // construct url path and request 
    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:restURL]; 
    [request setValue:MyRestfulModel.authorizationToken forHTTPHeaderField:@"Authorization"]; 
    [[[NSURLSession sharedSession] dataTaskWithRequest: request 
            completionHandler:^(NSData *data, NSURLResponse * response, NSError * error) { 
        NSHTTPResponse * httpResponse = (NSHTTPResponse *) response; 
        if([httpResponse statusCode] == 401) { 
         [LoginViewController showWithCompletion:^(NSString *username, NSString *token) { 
          NSLog(@"Retrying request after user reauthenticated"); 
          MyRestfulModel.authorizationToken = token; 
          [self doSomeRestfulCall:restURL completionHandler:callback]; 
         }]; 
         return; 
        } else { 
         callback(data); 
        } 
       }] resume]; 
} 

然後,登錄視圖控制器是很多奇蹟發生的地方。 (如果您不想使用自定義登錄控制器,也可以使用警報視圖,請查看此文章:http://nscookbook.com/2013/04/ios-programming-recipe-22-simplify-uialertview-with-blocks/

@interface LoginViewController 
@property (copy) void(^completionBlock)(NSString * username, NSString * tokenCredentials); 
@end 

@implementation LoginViewController 

+ (void) showWithCompletion: (void (^)(NSString * username, NSString * tokenCredentials))completion 
{ 
    AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    UIViewController * rootController = appDelegate.window.rootViewController; 
    UIStoryboard * storyboard = rootController.storyboard; 

    LoginViewController * controller = [storyboard instantiateViewControllerWithIdentifier:@"Login"]; 
    controller.completionBlock = completion; 

    controller.delegate = wrapper; 
    controller.modalPresentationStyle = UIModalPresentationFormSheet; 
    [rootController presentViewController:controller animated:YES completion:nil]; 
} 


// 
// <code for contacting your authentication service> 
// 


-(void) loginSuccessful: (NSString *) username withTokenCredentials:(NSString *)token 
{ 
    // <code to store username and token in the keychain> 

    if (self.completionBlock) 
     self.completionBlock(username, token); 
} 
相關問題