2014-03-01 78 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); 
} 
相关问题