用户登录后,您会获得一个令牌(摘要或 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
是否可以用更优雅的方式来解决这种情况。
答案 0 :(得分:1)
您当前的解决方案是有效的,并且您拥有相应的代码,可能有合理数量的代码来实现它,但该方法具有优点。
使用基于NSURLAuthenticationChallenge
的方法意味着在不同级别进行子类化,并使用setWillSendRequestForAuthenticationChallengeBlock:
扩充每个创建的操作。通常,这将是一种更好的方法,因为单个操作将用于执行整个操作,而不是必须复制它并更新细节,并且操作auth支持将执行auth任务而不是操作完成处理程序。这应该是较少的代码来维护,但是代码可能会被较少的人理解(或者大多数人需要更长时间才能理解),因此事情的维护方面可能会平衡所有代码。
如果您正在寻找优雅,那么请考虑改变,但鉴于您已经有了一个有效的解决方案,否则收效甚微。
答案 1 :(得分:0)
我正在搜索此问题的答案并"Matt", the creator of AFNetworking, suggest this:
我发现处理这个问题的最佳解决方案是使用依赖 NSOperations在任何之前检查有效的,未过期的令牌 允许传出请求通过。在那一点上,它取决于 开发人员确定刷新的最佳行动方案 令牌,或者首先获得一个新令牌。