Android - Retrofit 2 - Authenticator Result

こ雲淡風輕ζ 提交于 2019-11-29 03:46:36

问题


I'm trying to use Retrofit (2.0.0-beta3), but when using an Authenticator to add a token, I can't seem to get the data from the synchronous call. Our logging on the back-end just shows a lot of login attempts, but I can't get the data from the body to actually add to the header.

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

I'm not exactly too sure on why it's not even printing anything out. Any tips on how to solve this issue would be greatly appreciated, thanks for taking the time to help.


These are the sources I've been reading on how to implement Retrofit.

Using Authenticator:

  • https://stackoverflow.com/a/31624433/3106174

  • https://github.com/square/okhttp/wiki/Recipes#handling-authentication

Making synchronous calls with Retrofit 2:

  • https://futurestud.io/blog/retrofit-synchronous-and-asynchronous-requests

回答1:


I have similar authenticator and it works with 2.0.0-beta2.

If you get lots of login attempts from you Authenticator, I suggest make sure that when you make the synchronous call, you are not using Authenticator with that call. That could end up in loop, if also your "emailLogin" fails.

Also I would recommend adding loggingInterceptor to see all trafic to server: Logging with Retrofit 2




回答2:


I managed to get a decent solution using the TokenAuthenticator and an Interceptor and thought I'd share the idea as it may help some others.

Adding the 'TokenInterceptor' class that handles adding the token to the header is the token exists, and the 'TokenAuthenticator' class handles the case when there is no token, and we need to generate one.

I'm sure there are some better ways to implement this, but it's a good starting point I think.

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

Source:

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




回答3:


I know it's a late answer but for anyone still wondering how to Add / Refresh token with Retrofit 2 Authenticator, here is a working solution:

Note: preferenceHelper is your Preference Manager class where you set/get your shared preferences.

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

Also note:

  1. preferenceHelper and apiService needs to be provided in some way.
  2. This is not an example that will work for all systems and api's but an example in how adding and refreshing the token should be done using Retrofit 2 Authenticator


来源:https://stackoverflow.com/questions/35238894/android-retrofit-2-authenticator-result

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!