Как только ваш пользователь войдет в систему, вы получите токен (дайджест или oauth), который вы установили в свой заголовок авторизации HTTP, и который дает вам разрешение на доступ к вашему веб-сервис. Если вы сохраняете свое имя пользователя, пароль и этот токен где-то на телефоне (по умолчанию по умолчанию или предпочтительно в цепочке ключей), то ваш пользователь автоматически регистрируется каждый раз, когда приложение перезагружается.
Но что, если ваш токен истекает? Затем вам просто нужно запросить новый токен, и если пользователь не изменил свой пароль, он должен снова войти в систему автоматически.
Один из способов реализовать эту операцию обновления токена заключается в подклассе AFHTTPRequestOperation
и позаботиться о 401 неавторизованном HTTP-статусе, чтобы запросить новый токен. Когда выдается новый токен, вы можете снова вызвать неудачную операцию, которая теперь должна преуспеть.
Затем вы должны зарегистрировать этот класс, чтобы каждый запрос 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];
}
Пожалуйста, имейте в виду, что этот код не работает так, как есть, поскольку он полагается на внешний код, который зависит от вашего веб-API, вид авторизации (дайджест, клятва...), а также какую структуру вы используете через AFNetworking (например, RestKit).
Это довольно эффективно и хорошо зарекомендовало себя как с помощью дайджест, так и с помощью oauth, используя RestKit, привязанный к CoreDatastrong > (в этом случае RetryRequestOperation является подклассом RKManagedObjectRequestOperation
).
Теперь мой вопрос: это лучший способ обновить токен?
Мне действительно интересно, можно ли NSURLAuthenticationChallenge
решить эту ситуацию более элегантно.