Una volta effettuato l'accesso, si ottiene un token (digest o oauth) impostato nell'intestazione dell'autorizzazione HTTP e che consente l'accesso al servizio Web. Se si memorizzano il nome dell'utente, la password e questo token da qualche parte sul telefono (nelle impostazioni predefinite dell'utente, o preferibilmente nel portachiavi), l'utente viene automaticamente registrato ogni volta che si riavvia l'applicazione.La migliore soluzione per aggiornare automaticamente token con AFNetworking?
E se il token scade? Quindi "semplicemente" è necessario chiedere a un nuovo token e se l'utente non ha cambiato la sua password, allora dovrebbe essere registrato nuovamente automaticamente.
Un modo per implementare questo token operazione rinfrescante è quello di creare una sottoclasse AFHTTPRequestOperation
e prendersi cura di 401 codice di stato HTTP non autorizzato, al fine di chiedere un nuovo token. Quando viene emesso il nuovo token, è possibile richiamare ancora una volta l'operazione non riuscita che dovrebbe ora riuscire.
Quindi è necessario registrare questa classe in modo che ogni richiesta di AFNetworking (getPath, postPath, ...) utilizzi questa classe.
[httpClient registerHTTPOperationClass:[RetryRequestOperation class]]
Ecco un esempio di una tale classe:
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];
}
prega, essere consapevoli del fatto che questo codice non funziona così com'è, in quanto si basa sul codice esterno che dipende dal vostro web API, il tipo di autorizzazione (digest, giuramento, ...) e anche il tipo di framework che si utilizza su AFNetworking (RestKit ad esempio).
Questo è molto efficiente ed è dimostrato di funzionare bene sia digerire e autorizzazione OAuth utilizzando RestKit legato al CoreData (in questo caso RetryRequestOperation è una sottoclasse di RKManagedObjectRequestOperation
).
La mia domanda ora è: è questo il modo migliore per aggiornare un token? In realtà mi chiedo se sia possibile utilizzare NSURLAuthenticationChallenge
per risolvere questa situazione in modo più elegante.
Sono d'accordo con Wain sul fatto che questa implementazione sia più semplice da leggere e comprendere. Una piccola critica, però: AFNetworking garantisce che venga chiamato il blocco di successo o di fallimento. Il percorso del codice che raggiunge '[SessionManager logout]' viola questa garanzia. –
Grazie a @AaronBrager, il codice di esempio è stato corretto per quanto riguarda la tua osservazione accurata. – Phil
Man, sto usando anche RestKit + CoreData e tutto quello che voglio dire è che il tuo metodo è giusto. Grazie. – kokoko