Как только ваш пользователь вошел в систему, вы получаете токен ( дайджест или 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 , привязанного к CoreData (в этом случае RetryRequestOperation является подклассом RKManagedObjectRequestOperation
).
Мой вопрос сейчас: это лучший способ обновить токен? Мне действительно интересно, можно ли использовать NSURLAuthenticationChallenge
, чтобы разрешить эту ситуацию более элегантно.