【问题标题】:Android - Retrofit 2 - Authenticator ResultAndroid - 改造 2 - 身份验证器结果
【发布时间】:2016-05-16 07:12:47
【问题描述】:

我正在尝试使用 Retrofit (2.0.0-beta3),但是当使用 Authenticator 添加令牌时,我似乎无法从同步调用中获取数据。我们在后端的日志记录只是显示了很多登录尝试,但我无法从正文中获取数据以实际添加到标题中。

    public static class TokenAuthenticator implements Authenticator {
    @Override
    public Request authenticate(Route route, Response response) throws IOException {
        // Refresh your access_token using a synchronous api request
        UserService userService = createService(UserService.class);

        Call<Session> call = userService.emailLogin(new Credentials("handle", "pass"));

        // This call is made correctly, as it shows up on the back-end.
        Session body = call.execute().body();

        // This line is never hit.
        Logger.d("Session token: " + body.token);

        // Add new header to rejected request and retry it
        return response.request().newBuilder()
                .header("Auth-Token", body.token)
                .build();
        }
    }

我不太确定为什么它甚至没有打印出任何东西。非常感谢您提供有关如何解决此问题的任何提示,感谢您抽出时间提供帮助。


这些是我一直在阅读的关于如何实施改造的资料。

使用验证器:

使用 Retrofit 2 进行同步调用:

【问题讨论】:

  • 您确定 gson 的配置方式可以理解您的 Session 类吗?
  • 异步调用 emailLogin(...) 工作,所以我不认为这是 gson 的问题。有没有一种简单的方法可以判断该呼叫出了什么问题?我看不到任何错误或任何东西。

标签: android authentication retrofit2


【解决方案1】:

我设法使用 TokenAuthenticator 和 Interceptor 获得了一个不错的解决方案,并认为我会分享这个想法,因为它可能对其他人有所帮助。

添加'TokenInterceptor'类处理将token添加到header是token存在,'TokenAuthenticator'类处理没有token的情况,我们需要生成一个。

我确信有一些更好的方法可以实现这一点,但我认为这是一个很好的起点。

public static class TokenAuthenticator implements Authenticator {
    @Override
    public Request authenticate( Route route, Response response) throws IOException {
    ...
    Session body = call.execute().body();
    Logger.d("Session token: " + body.token);
    // Storing the token somewhere.
    session.token = body.token;
    ...
}


private static class TokenInterceptor implements Interceptor {
@Override
    public Response intercept( Chain chain ) throws IOException {
        Request originalRequest = chain.request();

        // Nothing to add to intercepted request if:
        // a) Authorization value is empty because user is not logged in yet
        // b) There is already a header with updated Authorization value
        if (authorizationTokenIsEmpty() || alreadyHasAuthorizationHeader(originalRequest)) {
            return chain.proceed(originalRequest);
        }

        // Add authorization header with updated authorization value to  intercepted request
        Request authorisedRequest = originalRequest.newBuilder()
                .header("Auth-Token", session.token )
                .build();
        return chain.proceed(authorisedRequest);
    }
}

来源:

http://lgvalle.xyz/2015/07/27/okhttp-authentication/

【讨论】:

    【解决方案2】:

    我有类似的身份验证器,它适用于 2.0.0-beta2。

    如果您从 Authenticator 获得大量登录尝试,我建议确保在进行同步调用时,您没有在该调用中使用 Authenticator。 如果您的“emailLogin”也失败了,那可能会陷入循环。

    另外我建议添加 loggingInterceptor 来查看所有到服务器的流量:Logging with Retrofit 2

    【讨论】:

    • 感谢您的回答,我已经得到它来实际添加令牌;但是,每次我尝试调用另一个网络调用时,它都会做同样的事情,所以我猜标题不会在调用之间结转。你如何保存/设置每个调用的标题?
    • 我专门将令牌存储到数据库中,并将令牌添加到所有调用的“授权”标头中。将此添加到您的调用中并将令牌作为参数提供:@Header("Authorization") String bearerToken,
    • 所以,我会将调用更改为“Call getUser(@Header("Authorization") String token);”,但是每次调用它时,我都必须通过在令牌中,这是有道理的。但是有没有更清洁的方法来做到这一点?也许使用拦截器或以某种方式包装函数?
    • @Jari Kokkonen 我们如何使用身份验证器处理刷新令牌过期??
    【解决方案3】:

    我知道这是一个迟到的答案,但对于仍然想知道如何使用 Retrofit 2 Authenticator 添加/刷新令牌的人来说,这是一个可行的解决方案:

    注意:preferenceHelper 是您的 Preference Manager 类,您可以在其中设置/获取您的共享偏好。

    public class AuthenticationHelper implements Authenticator {
    
        private static final String HEADER_AUTHORIZATION = "Authorization";
        private static final int REFRESH_TOKEN_FAIL = 403;
    
        private Context context;
    
        AuthenticationHelper(@ApplicationContext Context context) {
            this.context = context;
        }
    
        @Override
        public Request authenticate(@NonNull Route route, @NonNull Response response) throws IOException {
            // We need to have a token in order to refresh it.
            String token = preferencesHelper.getAccessToken();
            if (token == null)
                return null;
    
            synchronized (this) {
                String newToken = preferencesHelper.getAccessToken();
                if (newToken == null)
                    return null;
    
                // Check if the request made was previously made as an authenticated request.
                if (response.request().header(HEADER_AUTHORIZATION) != null) {
    
                    // If the token has changed since the request was made, use the new token.
                    if (!newToken.equals(token)) {
                        return response.request()
                                .newBuilder()
                                .removeHeader(HEADER_AUTHORIZATION)
                                .addHeader(HEADER_AUTHORIZATION, "Bearer " + newToken)
                                .build();
                    }
    
                    JsonObject refreshObject = new JsonObject();
                    refreshObject.addProperty("refreshToken", preferencesHelper.getRefreshToken());
    
                    retrofit2.Response<UserToken> tokenResponse = apiService.refreshToken(refreshObject).execute();
    
                    if (tokenResponse.isSuccessful()) {
    
                        UserToken userToken = tokenResponse.body();
                        if (userToken == null)
                            return null;
    
                        preferencesHelper.saveAccessToken(userToken.getToken());
                        preferencesHelper.saveRefreshToken(userToken.getRefreshToken());
    
    
                        // Retry the request with the new token.
                        return response.request()
                                .newBuilder()
                                .removeHeader(HEADER_AUTHORIZATION)
                                .addHeader(HEADER_AUTHORIZATION, "Bearer " + userToken.getToken())
                                .build();
                    } else {
                        if (tokenResponse.code() == REFRESH_TOKEN_FAIL) {
                            logoutUser();
                        }
                    }
                }
            }
            return null;
        }
    
        private void logoutUser() {
            // logout user
        }
    }
    

    另请注意:

    1. preferenceHelper 和 apiService 需要以某种方式提供。
    2. 这不是一个适用于所有系统和 api 的示例,而是一个如何使用 Retrofit 2 Authenticator 添加和刷新令牌的示例

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-07-12
      • 1970-01-01
      • 2020-08-13
      • 2017-09-23
      • 1970-01-01
      • 1970-01-01
      • 2014-04-05
      • 1970-01-01
      相关资源
      最近更新 更多