使用AFNetworking自动刷新令牌的最佳解决方案?

时间:2014-06-27 08:31:20

标签: ios authentication oauth restkit afnetworking

用户登录后,您会获得一个令牌(摘要 oauth ),您可以将其设置到您的HTTP授权标头中,并授予您访问权限的权限网络服务。 如果您将用户的姓名,密码和此令牌存储在手机的某个位置(用户默认值,或最好是在钥匙串中),那么每次应用程序重新启动时,您的用户都会自动登录。

但是如果您的令牌过期怎么办?然后你就是"简单地说#34;需要要求新令牌,如果用户没有更改密码,那么他应该再次登录自动

实施此令牌刷新操作的一种方法是继承AFHTTPRequestOperation并处理401 Unauthorized HTTP Status代码以便询问新令牌。发出新令牌后,您可以再次调用失败的操作,该操作现在应该成功。

然后您必须注册此类,以便每个AFNetworking请求(getPath,postPath,...)现在都使用此类。

[httpClient registerHTTPOperationClass:[RetryRequestOperation class]]

以下是这样一个类的例子:

static NSInteger const kHTTPStatusCodeUnauthorized = 401;

@interface RetryRequestOperation ()
@property (nonatomic, assign) BOOL isRetrying;
@end

@implementation RetryRequestOperation
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *, id))success
                              failure:(void (^)(AFHTTPRequestOperation *, NSError *))failure
{
    __unsafe_unretained RetryRequestOperation *weakSelf = self;

    [super setCompletionBlockWithSuccess:success failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // In case of a 401 error, an authentification with email/password is tried just once to renew the token.
        // If it succeeds, then the opration is sent again.
        // If it fails, then the failure operation block is called.
        if(([operation.response statusCode] == kHTTPStatusCodeUnauthorized)
           && ![weakSelf isAuthenticateURL:operation.request.URL]
           && !weakSelf.isRetrying)
        {
            NSString *email;
            NSString *password;

            email = [SessionManager currentUserEmail];
            password = [SessionManager currentUserPassword];
            // Trying to authenticate again before relaunching unauthorized request.
            [ServiceManager authenticateWithEmail:email password:password completion:^(NSError *logError) {
                if (logError == nil) {
                    RetryRequestOperation *retryOperation;

                    // We are now authenticated again, the same request can be launched again.
                    retryOperation = [operation copy];
                    // Tell this is a retry. This ensures not to retry indefinitely if there is still an unauthorized error.
                    retryOperation.isRetrying = YES;
                    [retryOperation setCompletionBlockWithSuccess:success failure:failure];
                    // Enqueue the operation.
                    [ServiceManager enqueueObjectRequestOperation:retryOperation];
                }
                else
                {
                    failure(operation, logError);
                    if([self httpCodeFromError:logError] == kHTTPStatusCodeUnauthorized)
                    {
                        // The authentication returns also an unauthorized error, user really seems not to be authorized anymore.
                        // Maybe his password has changed?
                        // Then user is definitely logged out to be redirected to the login view.
                        [SessionManager logout];
                    }
                }
            }];
        }
        else
        {
            failure(operation, error);
        }
    }];
}

- (BOOL)isAuthenticateURL:(NSURL *)url
{
    // The path depends on your implementation, can be "auth", "oauth/token", ...
    return [url.path hasSuffix:kAuthenticatePath];
}

- (NSInteger)httpCodeFromError:(NSError *)error
{
    // How you get the HTTP status code depends on your implementation.
    return error.userInfo[kHTTPStatusCodeKey];
}

请注意,此代码不能正常工作,因为它依赖于依赖于您的Web API的外部代码,授权类型(摘要,誓言......)以及您使用的哪种框架通过AFNetworking(例如RestKit)。

这非常有效,并且已证明使用与 CoreData 相关联的 RestKit 进行摘要和oauth授权(在这种情况下,RetryRequestOperation是{{1的子类) }})。

我现在的问题是:这是刷新令牌的最佳方法吗? 我实际上想知道RKManagedObjectRequestOperation是否可以用更优雅的方式来解决这种情况。

2 个答案:

答案 0 :(得分:1)

您当前的解决方案是有效的,并且您拥有相应的代码,可能有合理数量的代码来实现它,但该方法具有优点。

使用基于NSURLAuthenticationChallenge的方法意味着在不同级别进行子类化,并使用setWillSendRequestForAuthenticationChallengeBlock:扩充每个创建的操作。通常,这将是一种更好的方法,因为单个操作将用于执行整个操作,而不是必须复制它并更新细节,并且操作auth支持将执行auth任务而不是操作完成处理程序。这应该是较少的代码来维护,但是代码可能会被较少的人理解(或者大多数人需要更长时间才能理解),因此事情的维护方面可能会平衡所有代码。

如果您正在寻找优雅,那么请考虑改变,但鉴于您已经有了一个有效的解决方案,否则收效甚微。

答案 1 :(得分:0)

我正在搜索此问题的答案并"Matt", the creator of AFNetworking, suggest this

  

我发现处理这个问题的最佳解决方案是使用依赖   NSOperations在任何之前检查有效的,未过期的令牌   允许传出请求通过。在那一点上,它取决于   开发人员确定刷新的最佳行动方案   令牌,或者首先获得一个新令牌。