Android Retrofit 2.0刷新令牌

时间:2016-06-11 14:07:42

标签: android jackson retrofit

我正在使用$(e.target)Retrofit 2.0转换器与Rest API进行通信。某些请求需要令牌才能获得授权。如果我拥有的令牌已过期,我需要用另一个请求刷新它们,并重复上次因此而失败的请求。

我的问题:我是否每次都需要手动执行此操作,还是有任何方法可以自动执行此操作?

这是我目前实施它的方式:

TrackerService

Jackson

ServiceGateway

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;
    }
}

3 个答案:

答案 0 :(得分:31)

我在2-3个月前搜索了这个主题并找到了OkHttp 's Authenticator。你可以使用它。这里有一个链接:refreshing-oauth-token-using-retrofit-without-modifying-all-calls

它的工作方式如下:如果您的请求返回401,则Authenticator会移入,并刷新您的令牌。但请不要忘记return null或尝试任何限制。如果您没有限制,它会在刷新请求失败时尝试刷新多次,并在刷新令牌时发出同步请求。

我也有一个问题和答案 - 我自己通过搜索theese链接写的 - 关于刷新Oauth2令牌,也许你会看看它:

问题:android-retrofit2-refresh-oauth-2-token

答案:android-retrofit2-refresh-oauth-2-token-answer

此外:例如,如果您有令牌,则需要每3小时刷新一次。你也可以写一个Interceptor。在Interceptor中:比较时间并刷新令牌而不会收到任何401响应。

您可以阅读Interceptor页面:OkHttp Interceptors

你也可以看一看:OkHttp handling-authentication

我知道此处没有代码,但请尝试链接并编辑您的问题,然后我会帮助您。

答案 1 :(得分:0)

服务器返回401未经授权时,将调用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()
  

答案 2 :(得分:0)

<块引用>

这是刷新令牌验证器的实现

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 或设置任何尝试限制。如果你不限制,它会在你的刷新请求失败时尝试多次刷新。另外,在刷新令牌时发出同步请求。