Estamos usando o Retrofit em nosso aplicativo Android para se comunicar com um servidor seguro OAuth2. Tudo funciona muito bem, usamos o RequestInterceptor para incluir o token de acesso em cada chamada. No entanto, haverá momentos em que o token de acesso expirará e o token precisará ser atualizado. Quando o token expirar, a próxima chamada retornará com um código HTTP não autorizado, facilitando o monitoramento. Podemos modificar cada chamada de Retrofit da seguinte maneira: No retorno de chamada com falha, verifique o código de erro, se for igual a Não autorizado, atualize o token OAuth e repita a chamada de Retrofit. No entanto, para isso, todas as chamadas devem ser modificadas, o que não é uma solução de fácil manutenção e boa. Existe uma maneira de fazer isso sem modificar todas as chamadas de retrofit?
157
Respostas:
Por favor, não use
Interceptors
para lidar com autenticação.Atualmente, a melhor abordagem para lidar com a autenticação é usar a nova
Authenticator
API, projetada especificamente para essa finalidade .O OkHttp solicitará automaticamente as
Authenticator
credenciais quando uma resposta estiver401 Not Authorised
tentando novamente a última solicitação com falha .Anexar um
Authenticator
a umOkHttpClient
da mesma forma que você faz comInterceptors
Use este cliente ao criar seu
Retrofit
RestAdapter
fonte
TokenAuthenticator
depende de umaservice
classe. Aservice
classe depende de umaOkHttpClient
instância. Para criar umOkHttpClient
eu preciso doTokenAuthenticator
. Como posso quebrar esse ciclo? DoisOkHttpClient
s diferentes ? Eles vão ter diferentes pools de conexão ...Se você estiver usando o Retrofit > =
1.9.0
, poderá usar o novo Interceptor do OkHttp , que foi introduzido noOkHttp 2.2.0
. Você gostaria de usar um Application Interceptor , que permiteretry and make multiple calls
.Seu Interceptor pode se parecer com este pseudocódigo:
Depois de definir o seu
Interceptor
, crie umOkHttpClient
e adicione o interceptor como um Application Interceptor .E, finalmente, use isso
OkHttpClient
ao criar o seuRestAdapter
.Aviso: Como
Jesse Wilson
(da Square) menciona aqui , essa é uma quantidade perigosa de energia.Com isso dito, eu definitivamente acho que essa é a melhor maneira de lidar com algo assim agora. Se você tiver alguma dúvida, não hesite em perguntar em um comentário.
fonte
Se você tem, por exemplo, um Retrofit de
TokenService
que precisa dentro do seu,Authenticator
mas só deseja configurar um para oOkHttpClient
qual pode usar umTokenServiceHolder
como uma dependênciaTokenAuthenticator
. Você precisaria manter uma referência a ele no nível do aplicativo (singleton). Isso é fácil se você estiver usando o Dagger 2, caso contrário, apenas crie um campo de classe dentro do seu Aplicativo.No
TokenAuthenticator.java
Em
TokenServiceHolder.java
:Configuração do cliente:
Se você estiver usando o Dagger 2 ou uma estrutura semelhante de injeção de dependência, existem alguns exemplos nas respostas a esta pergunta
fonte
TokenService
classe é criada?refreshToken()
fromservice.refreshToken().execute();
. Não é possível encontrar sua implementação em qualquer lugar.Usar
TokenAuthenticator
uma resposta como @theblang é uma maneira correta de lidarrefresh_token
.Aqui está o meu implemento (usei Kotlin, Dagger, RX, mas você pode usar essa ideia para implantar no seu caso)
TokenAuthenticator
Para evitar o ciclo de dependência, como o comentário de @Brais Gabin, eu crio 2 interfaces como
e
AccessTokenWrapper
classeAccessToken
classeMy Interceptor
Por fim, adicione
Interceptor
eAuthenticator
ao seuOKHttpClient
serviço de criação PotoAuthApiDemo
https://github.com/PhanVanLinh/AndroidMVPKotlin
Nota
Fluxo do autenticadorgetImage()
Código de erro de retorno 401 da API de exemploauthenticate
método dentroTokenAuthenticator
será acionadononeAuthAPI.refreshToken(...)
chamadononeAuthAPI.refreshToken(...)
resposta -> novo token será adicionado ao cabeçalhogetImage()
será chamado automaticamente com o novo cabeçalho (HttpLogging
NÃO registrará esta chamada) (intercept
dentroAuthInterceptor
NÃO SERÁ CHAMADO )Se
getImage()
ainda falhar com o erro 401, oauthenticate
método internoTokenAuthenticator
será acionado novamente e novamente, em seguida, gerará erro sobre o método de chamada muitas vezes (java.net.ProtocolException: Too many follow-up requests
). Você pode evitá-lo pela resposta da contagem . Exemplo, se vocêreturn null
emauthenticate
após 3 vezes repetir,getImage()
vai terminar ereturn response 401
Se a
getImage()
resposta tiver êxito =>, o resultado será o resultado normalmente (como você chamagetImage()
sem erro)Espero que ajude
fonte
Eu sei que isso é um tópico antigo, mas apenas no caso de alguém tropeçar nele.
Eu estava enfrentando o mesmo problema, mas queria criar apenas um OkHttpClient porque não acho que precise de outro apenas para o próprio TokenAuthenticator, estava usando o Dagger2, então acabei fornecendo a classe de serviço como Lazy injetado no TokenAuthenticator, você pode ler mais sobre a injeção Lazy no punhal 2 aqui , mas é como dizer basicamente ao Dagger para NÃO criar o serviço necessário pelo TokenAuthenticator imediatamente.
Você pode consultar este encadeamento SO para obter um código de exemplo: Como resolver uma dependência circular enquanto ainda usa o Dagger2?
fonte
Você pode tentar criar uma classe base para todos os seus carregadores na qual seria capaz de capturar uma exceção específica e, em seguida, agir conforme necessário. Faça com que todos os seus diferentes carregadores se estendam da classe base, a fim de espalhar o comportamento.
fonte
Após uma longa pesquisa, eu customizei o cliente Apache para lidar com o Refreshing AccessToken For Retrofit No qual você envia o token de acesso como parâmetro.
Inicie seu adaptador com o cliente persistente de cookie
Cookie Cliente persistente que mantém cookies para todas as solicitações e verifica com cada resposta da solicitação, se for de acesso não autorizado ERROR_CODE = 401, atualize o token de acesso e recupere a solicitação, apenas processe a solicitação.
fonte
O uso de um interceptador (injetar o token) e um autenticador (operações de atualização) fazem o trabalho, mas:
Também tive um problema de chamada dupla: a primeira chamada sempre retornava um 401 : o token não era injetado na primeira chamada (interceptador) e o autenticador era chamado: duas solicitações foram feitas.
A correção foi apenas para reafirmar a solicitação para a compilação no Interceptor:
ANTES:
DEPOIS DE:
EM UM BLOCO:
Espero que ajude.
Edit: Eu não encontrei uma maneira de evitar a primeira chamada para sempre retornar 401 usando apenas o autenticador e nenhum interceptador
fonte
Para quem quisesse resolver chamadas simultâneas / paralelas ao atualizar o token. Aqui está uma solução alternativa
fonte