Android Retrofit 2.0 刷新令牌
Posted
技术标签:
【中文标题】Android Retrofit 2.0 刷新令牌【英文标题】:Android Retrofit 2.0 Refresh Tokens 【发布时间】:2016-10-12 09:23:45 【问题描述】:我正在使用 Retrofit 2.0
和 Jackson
转换器与 Rest API 进行通信。一些请求需要授权令牌。如果我拥有的令牌已过期,我需要用另一个请求刷新它们并重复上一个因此而失败的请求。
我的问题:我需要每次都手动完成还是有什么方法可以自动完成?
这是我目前实现它的方式:
TrackerService
public interface TrackerService
@POST("auth/sendPassword")
Call<ResponseMessage> sendPassword(@Header("app-type") String appType,
@Body User userMobile);
@FormUrlEncoded
@POST("oauth/token")
Call<TokenResponse> oathToken(@Field("client_id") String clientId,
@Field("client_secret") String clientSecret,
@Field("grant_type") String grantType,
@Field("username") String username,
@Field("password") String password);
@FormUrlEncoded
@POST("oauth/token")
Call<TokenResponse> refreshToken(@Field("client_id") String clientId,
@Field("client_secret") String clientSecret,
@Field("grant_type") String grantType,
@Field("refresh_token") String username);
@PUT("me/profile")
Call<Profile> updateProfile(@Header("app-type") String appType,
@Header("Authorization") String token,
@Body Profile profile);
服务网关
public class ServiceGateway
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit retrofit;
public static <S> S createService(Class<S> serviceClass)
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20 * 1000, TimeUnit.MILLISECONDS)
.writeTimeout(20 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(20 * 1000, TimeUnit.MILLISECONDS)
.addInterceptor(interceptor).build();
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(JacksonConverterFactory.create());
retrofit = builder.client(httpClient.build())
.client(client)
.build();
return retrofit.create(serviceClass);
public static Retrofit getRetrofit()
return retrofit;
令牌过期时如何调用函数和处理函数
trackerService = ServiceGateway.createService(TrackerService.class);
Call<Profile> call = trackerService.updateProfile(getString(R.string.app_type), "Bearer " + userPrefs.accessToken().get(),
new Profile(trimedInvitationMessage, title,
String.valueOf(selectedCountry.getCountryCode()), mobilePhone, countryISO, fullName));
call.enqueue(new Callback<Profile>()
@Override
public void onResponse(Call<Profile> call, Response<Profile> response)
if (response.body() != null)
else
if (response.raw().code() == 401)
Call<TokenResponse> refreshTokenCall = trackerService.refreshToken(userPrefs.clientId().get(),
userPrefs.clientSecret().get(), "refresh_token", userPrefs.refreshToken().get());
refreshTokenCall.enqueue(new Callback<TokenResponse>()
@Override
public void onResponse(Call<TokenResponse> call, Response<TokenResponse> response)
if (response.body() != null)
updateAdviserProfile(trimedInvitationMessage, title, mobilePhone, countryISO, fullName);
else
userPrefs.clear();
Intent intent = new Intent(WelcomeActivity_.launcher(EditProfileActivity.this));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
startActivity(WelcomeActivity_.launcher(EditProfileActivity.this));
@Override
public void onFailure(Call<TokenResponse> call, Throwable t)
);
else if (response.raw().code() == 422)
@Override
public void onFailure(Call<Profile> call, Throwable t)
);
【问题讨论】:
【参考方案1】:我从 2-3 个月前开始搜索这个主题,发现 OkHttp's Authenticator
。你可以使用它。这里有一个链接:refreshing-oauth-token-using-retrofit-without-modifying-all-calls
它的工作原理是这样的:如果您的请求返回 401
,那么 Authenticator
将进入并刷新您的令牌。但不要忘记return null
或设置任何尝试限制。如果你不限制,它会在你的刷新请求失败时尝试多次刷新。此外,刷新令牌时发出同步请求。
另外,我有一个关于刷新 Oauth2 令牌的问答(均由我自己编写):
问题:android-retrofit2-refresh-oauth-2-token
回答:android-retrofit2-refresh-oauth-2-token-answer
另外:例如,如果您有一个令牌并且您需要每 3 小时刷新一次。你也可以写一个Interceptor
。在Interceptor
:比较时间并刷新您的令牌,而不会收到任何401
响应。
Square 的 Interceptor
文档:OkHttp Interceptors
Square 的 Authenticator
文档:OkHttp handling-authentication
我知道这里没有代码,但请查看链接并编辑您的问题,然后我会尽力帮助您。
【讨论】:
PLUS ONE 用简单的语言解释了OKHttp Authenticator的作用。这很有帮助【参考方案2】:服务器返回 401 Unauthorized 时调用 authenticate() 方法。
用于调用 ApiFactory.retrofit("url").create(PostDataInterface::class.java) .refreshToken(refreshTokenRequest)),我们正在使用 execute() 使其成为同步调用。
如果刷新令牌状态为 0。添加您的功能以注销用户。
interface PostDataInterface
@POST("refreshUserToken")
fun refreshToken(@Body refreshTokenRequest: RefreshTokenRequest?): Call<RefreshTokenResponse?>?
class TokenAuthenticator : Authenticator
override fun authenticate(route: Route?, response: Response): Request?
// This is a synchronous call
val updatedToken = getNewToken()
return updatedToken?.let
response.request.newBuilder().header("Authorization", it)
.build()
private fun getNewToken(): String?
val refreshTokenRequest = RefreshTokenRequest(SharedPreferenceHelper.refreshToken)
val call = ApiFactory.retrofit(BuildConfig.BASEURL).create(PostDataInterface::class.java)
.refreshToken(refreshTokenRequest)
val authTokenResponse = call?.execute()?.body()
if (authTokenResponse?.status == 0)
//Logout User
AuthUtility.logout(true)
return authTokenResponse?.data?.token
在 Okhttp 客户端中添加验证器
private val client =
OkHttpClient().newBuilder()
.authenticator(TokenAuthenticator())
...
.build()
【讨论】:
Ok 但是获取新的刷新令牌后如何重新调用API? 它会自动调用【参考方案3】:这里是刷新令牌认证器的实现
class TokenAuthenticator(
val sharedPrefsHelper: SharedPrefsHelper,
private val identityService: IdentityService
) : Authenticator
override fun authenticate(route: Route?, response: Response): Request?
Log.d("TokenAuth Request:", "$response.body")
val refreshToken = sharedPrefsHelper[SharedPrefsHelper.PREF_KEY_AUTH_REFRESH_TOKEN, null]
if (refreshToken.isNullOrEmpty().not())
val requestFields = mutableMapOf<String, String>()
requestFields["refresh_token"] = refreshToken!!
requestFields["grant_type"] = "refresh_token"
try
val tokenResponse = runBlocking
identityService.getAuthToken(requestFields)
Log.d("TokenAuth Success:", "$tokenResponse")
tokenResponse.accessToken.let accessToken ->
sharedPrefsHelper.put(
SharedPrefsHelper.PREF_KEY_AUTH_TOKEN,
accessToken
)
sharedPrefsHelper.put(
SharedPrefsHelper.PREF_KEY_AUTH_REFRESH_TOKEN,
tokenResponse.refreshToken
)
return response.request.newBuilder()
.header("Authorization", "Bearer $accessToken")
.build()
catch (e: Exception)
Log.d("TokenAuth Error:", "$e")
return null
使用构建器配置它 -
return OkHttpClient.Builder()
.authenticator(TokenAuthenticator(sharedPrefsHelper, identityBaseUrl))
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(requestInterceptor).addInterceptor(logging)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
也正如@Yasin 建议的那样 -
不要忘记返回 null 或设置任何尝试限制。如果你不限制,它会在你的刷新请求失败时尝试多次刷新。此外,刷新令牌时发出同步请求。
【讨论】:
以上是关于Android Retrofit 2.0 刷新令牌的主要内容,如果未能解决你的问题,请参考以下文章
需要调用一个api来使用retrofit在android mvvm中刷新令牌,在哪里编写逻辑?