Używamy Retrofit w naszej aplikacji na Androida, aby komunikować się z serwerem zabezpieczonym OAuth2. Wszystko działa świetnie, używamy RequestInterceptor do dołączania tokenu dostępu do każdego wywołania. Jednak zdarzają się sytuacje, w których token dostępu wygaśnie i będzie wymagał odświeżenia. Po wygaśnięciu tokenu następne wywołanie zwróci z nieautoryzowanym kodem HTTP, więc jest to łatwe do monitorowania. Moglibyśmy zmodyfikować każde wywołanie Retrofit w następujący sposób: W wywołaniu zwrotnym błędu, sprawdź kod błędu, jeśli jest równy Unauthorized, odśwież token OAuth, a następnie powtórz wywołanie Retrofit. Jednak w tym celu wszystkie wywołania powinny zostać zmodyfikowane, co nie jest łatwym w utrzymaniu i dobrym rozwiązaniem. Czy można to zrobić bez modyfikowania wszystkich wywołań dotyczących modernizacji?
157
Odpowiedzi:
Nie używaj go
Interceptors
do uwierzytelniania.Obecnie najlepszym podejściem do obsługi uwierzytelniania jest użycie nowego
Authenticator
interfejsu API, zaprojektowanego specjalnie do tego celu .OkHttp zostanie automatycznie zapytać
Authenticator
o poświadczenia, gdy odpowiedź jest401 Not Authorised
Ponowna próba nie powiodła się ostatni wniosek z nimi.Dołącz
Authenticator
do wOkHttpClient
taki sam sposób, w jaki robisz to zInterceptors
Użyj tego klienta podczas tworzenia
Retrofit
RestAdapter
źródło
TokenAuthenticator
zależy odservice
klasy.service
Klasy zależy odOkHttpClient
instancji. Aby utworzyć plikOkHttpClient
, potrzebujęTokenAuthenticator
. Jak mogę przerwać ten cykl? Dwa różneOkHttpClient
? Będą mieli różne pule połączeń ...Jeśli używasz Retrofit > =,
1.9.0
możesz skorzystać z nowego Interceptora OkHttp , który został wprowadzony w . Chciałbyś użyć Application Interceptor , który na to pozwala .OkHttp 2.2.0
retry and make multiple calls
Twój Interceptor może wyglądać mniej więcej tak, jak ten pseudokod:
Po zdefiniowaniu
Interceptor
utwórzOkHttpClient
i dodaj przechwytywacz jako element przechwytujący aplikacji .I na koniec użyj tego
OkHttpClient
podczas tworzenia swojegoRestAdapter
.Ostrzeżenie: jak
Jesse Wilson
wspomina (z Square) tutaj , jest to niebezpieczna ilość mocy.Mając to na uwadze, zdecydowanie uważam, że to najlepszy sposób na teraz poradzenie sobie z czymś takim. Jeśli masz jakieś pytania, nie wahaj się zadać w komentarzu.
źródło
Jeśli masz, powiedzmy, Retrofit
TokenService
, którego potrzebujesz w swoim,Authenticator
ale chciałbyś skonfigurować tylko taki, dlaOkHttpClient
którego możesz użyćTokenServiceHolder
jako zależnościTokenAuthenticator
. Musiałbyś zachować odniesienie do niego na poziomie aplikacji (singleton). Jest to łatwe, jeśli używasz Daggera 2, w przeciwnym razie po prostu utwórz pole klasy w aplikacji.W
TokenAuthenticator.java
W
TokenServiceHolder.java
:Konfiguracja klienta:
Jeśli używasz Daggera 2 lub podobnego frameworka wstrzykiwania zależności, w odpowiedziach na to pytanie znajduje się kilka przykładów
źródło
TokenService
tworzona klasa?refreshToken()
fromservice.refreshToken().execute();
. Nigdzie nie mogę znaleźć jego implementacji.Użycie
TokenAuthenticator
odpowiedzi like @theblang jest poprawnym sposobem obsługirefresh_token
.Oto moje narzędzie (ja używam Kotlin, Dagger, RX ale możesz wykorzystać ten pomysł na implementację do swojego przypadku)
TokenAuthenticator
Aby zapobiec cyklowi zależności, jak komentarz @Brais Gabin, tworzę 2 interfejsy, takie jak
i
AccessTokenWrapper
klasaAccessToken
klasaMy Interceptor
Na koniec dodaj
Interceptor
iAuthenticator
do swojegoOKHttpClient
podczas tworzenia usługi PotoAuthApiPróbny
https://github.com/PhanVanLinh/AndroidMVPKotlin
Uwaga
Przepływ uwierzytelnianiagetImage()
kod błędu API zwrócił 401authenticate
Metoda wewnątrzTokenAuthenticator
zostanie zwolnionanoneAuthAPI.refreshToken(...)
wywołanenoneAuthAPI.refreshToken(...)
odpowiedzi -> nowy token zostanie dodany do nagłówkagetImage()
wywoła AUTO z nowym nagłówkiem (HttpLogging
NIE BĘDZIE zarejestrować tego połączenia) (intercept
wewnątrzAuthInterceptor
NIE BĘDZIE WYWOŁANE )Jeśli
getImage()
nadal nie powiodło się z błędem 401,authenticate
metoda wewnętrznaTokenAuthenticator
zostanie uruchomiona PONOWNIE i PONOWNIE, a następnie wiele razy (java.net.ProtocolException: Too many follow-up requests
) wyrzuci błąd dotyczący wywołania metody . Możesz temu zapobiec, licząc odpowiedź . Przykładowo, jeślireturn null
wauthenticate
po 3 razy powtórzyć,getImage()
będzie skończyć ireturn response 401
Jeśli
getImage()
odpowiedź zakończy się sukcesem => otrzymamy wynik normalnie (jak dzwoniszgetImage()
bez błędu)Mam nadzieję, że to pomoże
źródło
Wiem, że to stara nitka, ale na wszelki wypadek, gdyby ktoś się w nią potknął.
Miałem ten sam problem, ale chciałem utworzyć tylko jednego OkHttpClient, ponieważ nie sądzę, że potrzebuję innego tylko dla samego TokenAuthenticator, używałem Dagger2, więc ostatecznie zapewniłem klasę usługi jako Lazy wstrzykniętą w TokenAuthenticator, możesz przeczytać więcej o leniwym wstrzyknięciu w sztylet 2 tutaj , ale w zasadzie to tak, jakbyś powiedział Daggerowi, aby NIE iść i od razu tworzyć usługę potrzebną TokenAuthenticator.
Możesz odwołać się do tego wątku SO po przykładowy kod: Jak rozwiązać zależność cykliczną, nadal używając Dagger2?
źródło
Możesz spróbować utworzyć klasę bazową dla wszystkich programów ładujących, w której byłbyś w stanie złapać określony wyjątek, a następnie działać tak, jak potrzebujesz. Spraw, aby wszystkie różne programy ładujące rozszerzały się z klasy bazowej, aby rozpowszechnić zachowanie.
źródło
Po długich badaniach dostosowałem klienta Apache do obsługi odświeżania AccessToken For Retrofit, w którym wysyłasz token dostępu jako parametr.
Zainicjuj swój Adapter za pomocą trwałego klienta cookie
Cookie Trwały klient, który przechowuje pliki cookie dla wszystkich żądań i sprawdza przy każdej odpowiedzi na żądanie, jeśli jest to nieautoryzowany dostęp ERROR_CODE = 401, odświeża token dostępu i przywołuje żądanie, w przeciwnym razie tylko przetwarza żądanie.
źródło
Używając jednego Interceptora (wstrzyknij token) i jednego Authenticatora (operacje odświeżania) wykonują zadanie, ale:
Miałem też problem z podwójnym połączeniem: pierwsze połączenie zawsze zwracało 401 : token nie został wstrzyknięty przy pierwszym wywołaniu (przechwytywacz) i został wywołany element uwierzytelniający: wykonano dwa żądania.
Poprawka polegała tylko na ponownym wpłynięciu żądania na kompilację w Interceptorze:
PRZED:
PO:
W JEDNYM BLOKU:
Mam nadzieję, że to pomoże.
Edycja: nie znalazłem sposobu, aby uniknąć pierwszego wywołania zawsze zwracającego 401 przy użyciu tylko uwierzytelniacza i bez przechwytywacza
źródło
Do każdego, kto chciał rozwiązać współbieżne / równoległe wywołania podczas odświeżania tokenu. Oto obejście
źródło